synchronized?同步索绪,又稱同步鎖,以下簡(jiǎn)稱 "sync"凳寺。
雖然synchronized的寫(xiě)法有七八種(見(jiàn)下例)拥刻, 其實(shí)只分兩類(lèi)吴汪,一個(gè)對(duì)象實(shí)例鎖,一個(gè)類(lèi)鎖蒋腮。
對(duì)象實(shí)例鎖
1作彤、synchronized void method() :普通sync方法,鎖調(diào)用該方法的對(duì)象實(shí)現(xiàn)
2竭讳、synchronized(this) :sync this,鎖this
3、synchronized(objRef) :sync指定變量
4胰舆、synchronized(memberRef) :sync類(lèi)成員變量
5、synchronized(staticMemberRef) :sync靜態(tài)類(lèi)成員變量
類(lèi)鎖
6、synchronized static void method() :sync靜態(tài)方法
7光绕、synchronized(Sample.class) :sync類(lèi)鎖
8、synchronized(this.getClass()) :sync類(lèi)鎖
規(guī)則
1钙态、對(duì)象鎖與類(lèi)鎖互相獨(dú)立,每個(gè)實(shí)例馅袁、每個(gè)類(lèi)都只有一個(gè)鎖;
2、同為對(duì)象鎖的sync調(diào)用厌衔,判斷是否同一個(gè)對(duì)象引用,如同則存在同步競(jìng)爭(zhēng)变勇;注意Integer/String的緩存坑恤左,兩個(gè)變量實(shí)為一個(gè)。
3、同為類(lèi)鎖的sync調(diào)用飞袋,判斷是否同一個(gè)Class類(lèi)戳气,如同則存在同步競(jìng)爭(zhēng)。
4授嘀、非sync方法物咳,任何時(shí)候都不受同步鎖影響锣险。
5蹄皱、synchronized關(guān)鍵字是不能繼承的,它不是方法簽名芯肤。
6巷折、sync類(lèi)成員變量,該類(lèi)的不同實(shí)例持有的普通成員變量崖咨,對(duì)應(yīng)的是該成員變量的不同實(shí)例锻拘,它們之間是不存在鎖競(jìng)爭(zhēng)的。
7击蹲、sync靜態(tài)類(lèi)成員變量署拟,該類(lèi)的不同實(shí)例持有的是同一個(gè)成員變量實(shí)例,所以它們之間是存在鎖競(jìng)爭(zhēng)的歌豺。但與該靜態(tài)成員變量所在類(lèi)的類(lèi)鎖是互相獨(dú)立的推穷。
實(shí)現(xiàn)原理
sync同步鎖,是隱式鎖类咧,在進(jìn)入sync臨界區(qū)時(shí)由jvm自動(dòng)獲取馒铃,退出時(shí)自動(dòng)釋放。
sync同步鎖有三個(gè)屬性:
????依賴的對(duì)象實(shí)例/類(lèi)引用痕惋;(對(duì)象的鎖区宇,鎖的對(duì)象,同義)
????WaitSet值戳,等待池列表议谷,所有調(diào)用wait()方法的線程都會(huì)進(jìn)入該列表,等待池列表中的線程不會(huì)去競(jìng)爭(zhēng)sync鎖堕虹。待其他進(jìn)入sync臨界區(qū)的線程調(diào)用notify()后或wait(long)的時(shí)間到柿隙,會(huì)退出本區(qū)進(jìn)入EntryList區(qū);
????EntryList鲫凶,鎖池隊(duì)列禀崖,所有嘗試獲取sync鎖,進(jìn)入該鎖的EntryList螟炫,待當(dāng)前持有鎖的線程釋放后波附,按非公平方式分配鎖;
wait()方法只能sync臨界區(qū)內(nèi)使用,因?yàn)閣ait()的語(yǔ)義只有兩個(gè):一就是釋放sync鎖掸屡,未持有鎖談何釋放封寞;二是進(jìn)入WaitSet區(qū),等待notify或時(shí)間到仅财;
notify() 會(huì)從WaitSet列表中隨機(jī)喚醒一個(gè)狈究;
notifyAll()會(huì)將WaitSet列表中所有線程都喚醒,整體進(jìn)入鎖池去競(jìng)爭(zhēng)sync鎖盏求;
正確使用
wait() 應(yīng)配合while循環(huán)使用抖锥,不應(yīng)使用if,務(wù)必在wait()調(diào)用前后都檢查條件碎罚,如果不滿足磅废,必須調(diào)用notify()喚醒下一條線程來(lái)處理,自己回去繼續(xù)wait()直至條件滿足再往下執(zhí)行荆烈。
notifyAll()將所有WaitSet中的線程從等待池喚醒拯勉,全部進(jìn)入鎖池競(jìng)爭(zhēng)去sync鎖,最終也只有一個(gè)線程能獲取鎖去執(zhí)行憔购,喚醒+競(jìng)爭(zhēng)鎖池本身是線程上下文的重操作宫峦,對(duì)性能產(chǎn)生不良影響。濫用notifyAll()有可能導(dǎo)致“驚群效應(yīng)”玫鸟。
notify() 是對(duì)notifyAll()的一個(gè)優(yōu)化导绷,但它有很精確的應(yīng)用場(chǎng)景,并且要求正確使用鞋邑。不然可能導(dǎo)致死鎖诵次。正確的場(chǎng)景應(yīng)該是 WaitSet中等待的是相同的條件,喚醒任一個(gè)都能正確處理接下來(lái)的事項(xiàng)枚碗,如果喚醒的線程無(wú)法正確處理逾一,務(wù)必確保繼續(xù)notify()下一個(gè)線程,并且自身需要重新回到WaitSet中(參見(jiàn)上一條)肮雨。導(dǎo)致死鎖的原因是notify()隨機(jī)喚醒了一條線程遵堵,但它既無(wú)法正確改變條件,也不叫醒另一個(gè)兄弟來(lái)搞怨规,就會(huì)產(chǎn)生一個(gè)情況:鎖池中的隊(duì)列空了陌宿,等待池中有一堆線程,但不會(huì)再被喚醒永遠(yuǎn)等待波丰。
參考
? ? ? ??《Java編程思想》之《notify()vs.notifyAll()》