wait():使一個線程處于等待狀態(tài),并且釋放所持有的對象的lock。
sleep():使一個正在運行的線程處于睡眠狀態(tài),是一個靜態(tài)方法,調(diào)用此方法要捕捉InterruptedException異常
notify():喚醒一個處于等待狀態(tài)的線程,注意的是在調(diào)用此方法的時候,并不能確切的喚醒某一個等待狀態(tài)的線
程,而是由JVM確定喚醒哪個線程,而且不是按優(yōu)先級。
Allnotity():喚醒所有處入等待狀態(tài)的線程,注意并不是給所有喚醒線程一個對象的鎖,而是讓它們競爭。
實現(xiàn)同步機(jī)制有兩個方法:
1、同步代碼塊:
synchronized(同一個數(shù)據(jù)){} 同一個數(shù)據(jù):就是N條線程同時訪問一個數(shù)據(jù)。
2、同步方法:
public synchronized 數(shù)據(jù)返回類型 方法名(){}
就是使用 synchronized 來修飾某個方法,則該方法稱為同步方法。對于同步方法而言,無需顯示指定同步監(jiān)視器,同步方法的同步監(jiān)視器是 this 也就是該對象的本身(這里指的對象本身有點含糊,其實就是調(diào)用該同步方法的對象)通過使用同步方法,可非常方便的將某類變成線程安全的類,具有如下特征:
1,該類的對象可以被多個線程安全的訪問。
2,每個線程調(diào)用該對象的任意方法之后,都將得到正確的結(jié)果。
3,每個線程調(diào)用該對象的任意方法之后,該對象狀態(tài)依然保持合理狀態(tài)。
注:synchronized關(guān)鍵字可以修飾方法,也可以修飾代碼塊,但不能修飾構(gòu)造器,屬性等。
實現(xiàn)同步機(jī)制注意以下幾點: 安全性高,性能低,在多線程用。性能高,安全性低,在單線程用。
1,不要對線程安全類的所有方法都進(jìn)行同步,只對那些會改變共享資源方法的進(jìn)行同步。
2,如果可變類有兩種運行環(huán)境,當(dāng)線程環(huán)境和多線程環(huán)境則應(yīng)該為該可變類提供兩種版本:線程安全版本和線程不安全版本(沒有同步方法和同步塊)。在單線程中環(huán)境中,使用線程不安全版本以保證性能,在多線程中使用線程安全版本.
線程有可能和其他線程共享一些資源,比如,內(nèi)存,文件,數(shù)據(jù)庫等。
當(dāng)多個線程同時讀寫同一份共享資源的時候,可能會引起沖突。這時候,我們需要引入線程“同步”機(jī)制,即各位線程之間要有個先來后到,不能一窩蜂擠上去搶作一團(tuán)。
線程同步的真實意思和字面意思恰好相反。線程同步的真實意思,其實是“排隊”:幾個線程之間要排隊,一個一個對共享資源進(jìn)行操作,而不是同時進(jìn)行操作。
線程同步的方法
(1)wait():使一個線程處于等待狀態(tài),并且釋放所持有的對象的lock。
(2)sleep():使一個正在運行的線程處于睡眠狀態(tài),是一個靜態(tài)方法,調(diào)用此方法要捕捉
interruptedexception異常。
(3)notify():喚醒一個處于等待狀態(tài)的線程,注意的是在調(diào)用此方法的時候,并不能確切的
喚醒某一個等待狀態(tài)的線程,而是由jvm確定喚醒哪個線程,而且不是按優(yōu)先級。
(4)notityall ():喚醒所有處入等待狀態(tài)的線程,注意并不是給所有喚醒線程一個對象的鎖,
而是讓它們競爭
1 wait方法:
該方法屬于Object的方法,wait方法的作用是使得當(dāng)前調(diào)用wait方法所在部分(代碼塊)的線程停止執(zhí)行,并釋放當(dāng)前獲得的調(diào)用wait所在的代碼塊的鎖,并在其他線程調(diào)用notify或者notifyAll方法時恢復(fù)到競爭鎖狀態(tài)(一旦獲得鎖就恢復(fù)執(zhí)行)。
調(diào)用wait方法需要注意幾點:
第一點:wait被調(diào)用的時候必須在擁有鎖(即synchronized修飾的)的代碼塊中。
第二點:恢復(fù)執(zhí)行后,從wait的下一條語句開始執(zhí)行,因而wait方法總是應(yīng)當(dāng)在while循環(huán)中調(diào)用,以免出現(xiàn)恢復(fù)執(zhí)行后繼續(xù)執(zhí)行的條件不滿足卻繼續(xù)執(zhí)行的情況。
第三點:若wait方法參數(shù)中帶時間,則除了notify和notifyAll被調(diào)用能激活處于wait狀態(tài)(等待狀態(tài))的線程進(jìn)入鎖競爭外,在其他線程中interrupt它或者參數(shù)時間到了之后,該線程也將被激活到競爭狀態(tài)。
第四點:wait方法被調(diào)用的線程必須獲得之前執(zhí)行到wait時釋放掉的鎖重新獲得才能夠恢復(fù)執(zhí)行。
2 notify方法和notifyAll方法:
notify方法通知調(diào)用了wait方法,但是尚未激活的一個線程進(jìn)入線程調(diào)度隊列(即進(jìn)入鎖競爭),注意不是立即執(zhí)行。并且具體是哪一個線程不能保證。另外一點就是被喚醒的這個線程一定是在等待wait所釋放的鎖。
notifyAll方法則喚醒所有調(diào)用了wait方法,尚未激活的進(jìn)程進(jìn)入競爭隊列。
3 synchronized關(guān)鍵字:
第一點:synchronized用來標(biāo)識一個普通方法時,表示一個線程要執(zhí)行該方法,必須取得該方法所在的對象的鎖。
第二點:synchronized用來標(biāo)識一個靜態(tài)方法時,表示一個線程要執(zhí)行該方法,必須獲得該方法所在的類的類鎖。
第三點:synchronized修飾一個代碼塊。類似這樣:synchronized(obj) { //code。. }。表示一個線程要執(zhí)行該代碼塊,必須獲得obj的鎖。這樣做的目的是減小鎖的粒度,保證當(dāng)不同塊所需的鎖不沖突時不用對整個對象加鎖。利用零長度的byte數(shù)組對象做obj非常經(jīng)濟(jì)。
線程同步的方式包括:互斥鎖、讀寫鎖、條件變量、信號量和令牌。
以Java語言為例:用synchronized關(guān)鍵字修飾同步方法。同步有幾種實現(xiàn)方法分別是synchronized,wait與notifywait():使一個線程處于等待狀態(tài),并且釋放所持有的對象的lock。
sleep():使一個正在運行的線程處于睡眠狀態(tài),是一個靜態(tài)方法,調(diào)用此方法要捕捉InterruptedException異常。notify():喚醒一個處于等待狀態(tài)的線程,注意的是在調(diào)用此方法的時候,并不能確切的喚醒某一個等待狀態(tài)的線程,而是由JVM確定喚醒哪個線程,而且不是按優(yōu)先級。
Allnotity():喚醒所有處入等待狀態(tài)的線程,注意并不是給所有喚醒線程一個對象的鎖,而是讓它們競爭。同步是多線程中的重要概念。
同步的使用可以保證在多線程運行的環(huán)境中,程序不會產(chǎn)生設(shè)計之外的錯誤結(jié)果。同步的實現(xiàn)方式有兩種,同步方法和同步塊,這兩種方式都要用到synchronized關(guān)鍵字。
給一個方法增加synchronized修飾符之后就可以使它成為同步方法,這個方法可以是靜態(tài)方法和非靜態(tài)方法,但是不能是抽象類的抽象方法,也不能是接口中的接口方法。下面代碼是一個同步方法的示例:public synchronized void aMethod() { // do something } public static synchronized void anotherMethod() { // do something } 線程在執(zhí)行同步方法時是具有排它性的。
當(dāng)任意一個線程進(jìn)入到一個對象的任意一個同步方法時,這個對象的所有同步方法都被鎖定了,在此期間,其他任何線程都不能訪問這個對象的任意一個同步方法,直到這個線程執(zhí)行完它所調(diào)用的同步方法并從中退出,從而導(dǎo)致它釋放了該對象的同步鎖之后。在一個對象被某個線程鎖定之后,其他線程是可以訪問這個對象的所有非同步方法的。
同步塊是通過鎖定一個指定的對象,來對同步塊中包含的代碼進(jìn)行同步;而同步方法是對這個方法塊里的代碼進(jìn)行同步,而這種情況下鎖定的對象就是同步方法所屬的主體對象自身。如果這個方法是靜態(tài)同步方法呢?那么線程鎖定的就不是這個類的對象了,也不是這個類自身,而是這個類對應(yīng)的java.lang.Class類型的對象。
同步方法和同步塊之間的相互制約只限于同一個對象之間,所以靜態(tài)同步方法只受它所屬類的其它靜態(tài)同步方法的制約,而跟這個類的實例(對象)沒有關(guān)系。
wait():使一個線程處于等待狀態(tài),并且釋放所持有的對象的lock。
sleep():使一個正在運知行的線程處于睡眠狀態(tài),是一個靜態(tài)方法,調(diào)用此道方法要捕捉InterruptedException異常。notify():喚醒一個處于等待狀態(tài)的線程,注意的是在調(diào)用此方法的時候,并不專能確切的喚醒某一個等待狀態(tài)的線程,而是由JVM確定喚醒哪個線程,而且不是按優(yōu)先級。
Allnotity():喚醒所有處入等待狀態(tài)的線程,注意并不是給所有喚醒線程一個對象的鎖,而是讓屬它們競爭。
進(jìn)程中線程同步的四種常用方式: 1、臨界區(qū)(CCriticalSection) 當(dāng)多個線程訪問一個獨占性共享資源時,可以使用臨界區(qū)對象。
擁有臨界區(qū)的線程可以訪問被保護(hù)起來的資源或代碼段,其他線程若想訪問,則被掛起,直到擁有臨界區(qū)的線程放棄臨界區(qū)為止。具體應(yīng)用方式: 1、定義臨界區(qū)對象CcriticalSection g_CriticalSection; 2、在訪問共享資源(代碼或變量)之前,先獲得臨界區(qū)對象,g_CriticalSection.Lock(); 3、訪問共享資源后,則放棄臨界區(qū)對象,g_CriticalSection.Unlock(); 2、事件(CEvent) 事件機(jī)制,則允許一個線程在處理完一個任務(wù)后,主動喚醒另外一個線程執(zhí)行任務(wù)。
比如在某些網(wǎng)絡(luò)應(yīng)用程序中,一個線程如A負(fù)責(zé)偵聽通信端口,另外一個線程B負(fù)責(zé)更新用戶數(shù)據(jù),利用事件機(jī)制,則線程A可以通知線程B何時更新用戶數(shù)據(jù)。每個Cevent對象可以有兩種狀態(tài):有信號狀態(tài)和無信號狀態(tài)。
Cevent類對象有兩種類型:人工事件和自動事件。 自動事件對象,在被至少一個線程釋放后自動返回到無信號狀態(tài); 人工事件對象,獲得信號后,釋放可利用線程,但直到調(diào)用成員函數(shù)ReSet()才將其設(shè)置為無信號狀態(tài)。
在創(chuàng)建Cevent對象時,默認(rèn)創(chuàng)建的是自動事件。 1、1234CEvent(BOOL bInitiallyOwn=FALSE, BOOL bManualReset=FALSE, LPCTSTR lpszName=NULL, LPSECURITY_ATTRIBUTES lpsaAttribute=NULL); bInitiallyOwn:指定事件對象初始化狀態(tài),TRUE為有信號,F(xiàn)ALSE為無信號; bManualReset:指定要創(chuàng)建的事件是屬于人工事件還是自動事件。
TRUE為人工事件,F(xiàn)ALSE為自動事件; 后兩個參數(shù)一般設(shè)為NULL,在此不作過多說明。 2、BOOL CEvent::SetEvent(); 將Cevent類對象的狀態(tài)設(shè)置為有信號狀態(tài)。
如果事件是人工事件,則Cevent類對象保持為有信號狀態(tài),直到調(diào)用成員函數(shù)ResetEvent()將其重新設(shè)為無信號狀態(tài)時為止。如果為自動事件,則在SetEvent()后將事件設(shè)置為有信號狀態(tài),由系統(tǒng)自動重置為無信號狀態(tài)。
3、BOOL CEvent::ResetEvent(); 將事件的狀態(tài)設(shè)置為無信號狀態(tài),并保持該狀態(tài)直至SetEvent()被調(diào)用為止。由于自動事件是由系統(tǒng)自動重置,故自動事件不需要調(diào)用該函數(shù)。
一般通過調(diào)用WaitForSingleObject()函數(shù)來監(jiān)視事件狀態(tài)。 3、互斥量(CMutex) 互斥對象和臨界區(qū)對象非常相似,只是其允許在進(jìn)程間使用,而臨界區(qū)只限制與同一進(jìn)程的各個線程之間使用, 但是更節(jié)省資源,更有效率。
4、信號量(CSemphore) 當(dāng)需要一個計數(shù)器來限制可以使用某共享資源的線程數(shù)目時,可以使用“信號量”對象。CSemaphore類對象保存了對當(dāng)前訪問某一個指定資源的線程的計數(shù)值,該計數(shù)值是當(dāng)前還可以使用該資源的線程數(shù)目。
如果這個計數(shù)達(dá)到了零,則所有對這個CSemaphore類對象所控制的資源的訪問嘗試都被放入到一個隊列中等待,直到超時或計數(shù)值不為零為止。 CSemaphore( LONG lInitialCount = 1, LONG lMaxCount = 1, LPCTSTR pstrName = NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL ); lInitialCount:信號量對象的初始計數(shù)值,即可訪問線程數(shù)目的初始值; lMaxCount:信號量對象計數(shù)值的最大值,該參數(shù)決定了同一時刻可訪問由信號量保護(hù)的資源的線程最大數(shù)目; 后兩個參數(shù)在同一進(jìn)程中使用一般為NULL,不作過多討論; 一般是將當(dāng)前可用資源計數(shù)設(shè)置為最大資源計數(shù),每增加一個線程對共享資源的訪問,當(dāng)前可用資源計數(shù)就減1,只要當(dāng)前可用資源計數(shù)大于0,就可以發(fā)出信號量信號。
如果為0,則放入一個隊列中等待。線程在處理完共享資源后,應(yīng)在離開的同時通過ReleaseSemaphore()函數(shù)將當(dāng)前可用資源數(shù)加1。
聲明:本網(wǎng)站尊重并保護(hù)知識產(chǎn)權(quán),根據(jù)《信息網(wǎng)絡(luò)傳播權(quán)保護(hù)條例》,如果我們轉(zhuǎn)載的作品侵犯了您的權(quán)利,請在一個月內(nèi)通知我們,我們會及時刪除。
蜀ICP備2020033479號-4 Copyright ? 2016 學(xué)習(xí)鳥. 頁面生成時間:2.619秒