為什么要線程同步,說出線程同步的幾種方法
線程有時候回和其他線程共享一些資源,比如內存、數據庫等。當多個線程同時讀寫同一份共享資源的時候,可能會發生沖突。這時候,我們就需要引入線程“同步”機制,即各位線程之間要有順序使用,不能雜亂無章隨意使用。
線程同步的方法
1、wait():使一個線程處于等待狀態,并且釋放所持有的對象的lock。
2、sleep():使一個正在運行的線程處于睡眠狀態,是一個靜態方法,調用此方法要捕捉InterruptedException異常。
3、notify():喚醒一個處于等待狀態的線程,注意的是在調用此方法的時候,并不能確切的喚醒某一個等待狀態的線程,而是由JVM確定喚醒哪個線程,而且不是按優先級。
4、notityAll ():喚醒所有處入等待狀態的線程,注意并不是給所有喚醒線程一個對象的鎖,而是讓它們競爭。
擴展資料:
在一般情況下,創建一個線程是不能提高程序的執行效率的,所以要創建多個線程。但是多個線程同時運行的時候可能調用線程函數,在多個線程同時對同一個內存地址進行寫入,由于CPU時間調度上的問題,寫入數據會被多次的覆蓋,所以就要使線程同步。
在多線程編程里面,一些敏感數據不允許被多個線程同時訪問,此時就使用同步訪問技術,保證數據在任何時刻,最多有一個線程訪問,以保證數據的完整性。
參考資料:百度百科-線程同步
什么是線程的同步?為什么要實現線程的同步?
線程同步:是多個線程同時訪問同一資源,等待資源訪問結束,浪費時間,效率低
線程異步:訪問資源時在空閑等待時同時訪問其他資源,實現多線程機制
異步處理就是,你現在問我問題,我可以不回答你,等我用時間了再處理你這個問題.同步不就反之了,同步信息被立即處理 -- 直到信息處理完成才返回消息句柄;異步信息收到后將在后臺處理一段時間 -- 而早在信息處理結束前就返回消息句柄
區別同步和異步
一個進程啟動的多個不相干線程,它們相互之間關系為異步。
同步必須執行到底之后才能執行其他操作,而異步可以任意操作
同步的好處與弊端
好處:解決了線程的安全問題。
弊端:每次都有判斷鎖,降低了效率。
但是在安全與效率之間,首先考慮的是安全。
同步的前提
一、多個線程執行的時候需要同步,如果是單線程則不需要同步。
二、多個線程在執行的過程中是不是使用同一把鎖。如果是,就是同步。否則不是同步。
synchronzied(obj){ }同一個所
synchronzied(new Object){ } 不是同一個鎖
對run()中需要同步的代碼進行同步,如果有的代碼不需要同步,則不要放到同步代碼塊中去。
同步的表現形式有兩種:
1、同步代碼塊,被同步關鍵字封裝的代碼就是同步代碼塊;
2、同步函數,被同步關鍵字修飾的函數就是同步函數。
同步代碼塊的鎖是可以是任意對象,在執行之前就好創建好一個鎖對象。那么同步函數的鎖在哪里?
同步函數的鎖就是調用該同步函數的對象,也就是this。
如果同步函數被static修飾,那么該同步函數的鎖就是這個類在堆內存中形成的類文件對象。
這時候不一定有該類的對象,但一定有該類的字節碼文件對象。
java中線程同步的幾種方法
線程同步主要有以下種方法(示例中是實現計數的功能):
1、同步方法,即使用synchronized關鍵字修飾方法,例如:
publicsynchronizedvoidadd(intc){...}2、同步代碼塊,即有synchronized關鍵字修飾的語句塊,例如:
publicvoidaddAndGet(intc){
synchronized(this){
count+=c;
}
}3、使用特殊域變量(volatile)實現線程同步,該方法不能保證絕對的同步。
例如:privatevolatileintcount=0;
4、使用鎖實現線程同步,例如:
privateLocklock=newReentrantLock();
publicvoidadd(intc){
lock.lock();//上鎖
try{
count+=c;
}finally{
lock.unlock();//解鎖
}
}
5、使用原子變量實現線程同步,在java的util.concurrent.atomic包中提供了創建了原子類型變量的工具類,例如:
privateAtomicIntegercount=newAtomicInteger(1);
publicvoidadd(intc){
count.addAndGet(c);
}
6、使用局部變量實現線程同步,如果使用ThreadLocal管理變量,則每一個使用該變量的線程都獲得該變量的副本, 副本之間相互獨立,這樣每一個線程都可以隨意修改自己的變量副本,而不會對其他線程產生影響。
ThreadLocal 類的常用方法
new ThreadLocal<T>() : 創建一個線程本地變量
get() : 返回此線程局部變量的當前線程副本中的值
initialValue() : 返回此線程局部變量的當前線程的"初始值"
t(T value) : 將此線程局部變量的當前線程副本中的值設置為value
示例代碼:
privatestaticThreadLocal<Integer>count=newThreadLocal<Integer>(){
@Override
protectedIntegerinitialValue(){
return1;
}
};
publicvoidadd(intc){
count.t(count.get()+c);
}7、使用阻塞隊列實現,例如LinkedBlockingQueue,具體使用可百度LinkedBlockingQueue的用法或查看java文檔。
線程同步幾種方式
進程中線程同步的四種常用方式:
1、 臨界區(CCriticalSection)
當多個線程訪問一個獨占性共享資源時,可以使用臨界區對象。擁有臨界區的線程可以訪問被保護起來的資源或代碼段,其他線程若想訪問,則被掛起,直到擁有臨界區的線程放棄臨界區為止。具體應用方式:
1、 定義臨界區對象CcriticalSection g_CriticalSection;
2、 在訪問共享資源(代碼或變量)之前,先獲得臨界區對象,g_CriticalSection.Lock();
3、 訪問共享資源后,則放棄臨界區對象,g_CriticalSection.Unlock();
2、 事件(CEvent)
事件機制,則允許一個線程在處理完一個任務后,主動喚醒另外一個線程執行任務。比如在某些網絡應用程序中,一個線程如A負責偵聽通信端口,另外一個線程B負責更新用戶數據,利用事件機制,則線程A可以通知線程B何時更新用戶數據。每個Cevent對象可以有兩種狀態:有信號狀態和無信號狀態。Cevent類對象有兩種類型:人工事件和自動事件。
自動事件對象,在被至少一個線程釋放后自動返回到無信號狀態;
人工事件對象,獲得信號后,釋放可利用線程,但直到調用成員函數ReSet()才將其設置為無信號狀態。在創建Cevent對象時,默認創建的是自動事件。
1、1234CEvent(BOOL bInitiallyOwn=FALSE, BOOL bManualRet=FALSE, LPCTSTR lpszName=NULL, LPSECURITY_ATTRIBUTES lpsaAttribute=NULL);
bInitiallyOwn:指定事件對象初始化狀態,TRUE為有信號,FALSE為無信號;
bManualRet:指定要創建的事件是屬于人工事件還是自動事件。TRUE為人工事件,FALSE為自動事件;
后兩個參數一般設為NULL,在此不作過多說明。
2、BOOL CEvent::SetEvent();
將Cevent類對象的狀態設置為有信號狀態。如果事件是人工事件,則Cevent類對象保持為有信號狀態,直到調用成員函數RetEvent()將其重新設為無信號狀態時為止。如果為自動事件,則在SetEvent()后將事件設置為有信號狀態,由系統自動重置為無信號狀態。
3、BOOL CEvent::RetEvent();
將事件的狀態設置為無信號狀態,并保持該狀態直至SetEvent()被調用為止。由于自動事件是由系統自動重置,故自動事件不需要調用該函數。
一般通過調用WaitForSingleObject()函數來監視事件狀態。
3、 互斥量(CMutex)
互斥對象和臨界區對象非常相似,只是其允許在進程間使用,而臨界區只限制與同一進程的各個線程之間使用,
但是更節省資源,更有效率。
4、 信號量(CSemphore)
當需要一個計數器來限制可以使用某共享資源的線程數目時,可以使用“信號量”對象。CSemaphore類對象保存了對當前訪問某一個指定資源的線程的計數值,該計數值是當前還可以使用該資源的線程數目。如果這個計數達到了零,則所有對這個CSemaphore類對象所控制的資源的訪問嘗試都被放入到一個隊列中等待,直到超時或計數值不為零為止。
CSemaphore(
LONG lInitialCount = 1,
LONG lMaxCount = 1,
LPCTSTR pstrName = NULL,
LPSECURITY_ATTRIBUTES lpsaAttributes = NULL
);
lInitialCount:信號量對象的初始計數值,即可訪問線程數目的初始值;
lMaxCount:信號量對象計數值的最大值,該參數決定了同一時刻可訪問由信號量保護的資源的線程最大數目;
后兩個參數在同一進程中使用一般為NULL,不作過多討論;
一般是將當前可用資源計數設置為最大資源計數,每增加一個線程對共享資源的訪問,當前可用資源計數就減1,只要當前可用資源計數大于0,就可以發出信號量信號。如果為0,則放入一個隊列中等待。線程在處理完共享資源后,應在離開的同時通過ReleaSemaphore()函數將當前可用資源數加1。
線程同步在多線程編程中有什么作用?
線程同步是多線程編程中重要的概念。它的基本意思就是同步各個線程對資源(比如全局變量、文件)的訪問。如果不對資源訪問進行線程同步,就會產生資源訪問沖突的問題。比如,一個線程正在讀取一個全局變量,而讀取全局變量的這個語句在C++語言中只是一條語句,但在CPU指令處理這個過程的時候需要用多條指令來處理這個讀取變量的過程。如果這一系列指令被另外一個線程打斷了,也就是說CPU還沒有執行完全部讀取變量的所有指令就去執行另外一個線程了,而另外一個線程卻要對這個全局變量進行修改,修改完后又返回原先的線程,繼續執行讀取變量的指令,此時變量的值已經改變了,這樣第一個線程的執行結果就不是預料的結果了。線程同步是多線程編程中重要的概念。它的基本意思就是同步各個線程對資源(比如全局變量、文件)的訪問。如果不對資源訪問進行線程同步,就會產生資源訪問沖突的問題。比如,一個線程正在讀取一個全局變量,而讀取全局變量的這個語句在C++語言中只是一條語句,但在CPU指令處理這個過程的時候需要用多條指令來處理這個讀取變量的過程。如果這一系列指令被另外一個線程打斷了,也就是說CPU還沒有執行完全部讀取變量的所有指令就去執行另外一個線程了,而另外一個線程卻要對這個全局變量進行修改,修改完后又返回原先的線程,繼續執行讀取變量的指令,此時變量的值已經改變了,這樣第一個線程的執行結果就不是預料的結果了。