摘要:就像是擁有一個特殊房間的建筑,在同一時間里,這間特殊的房間只能被一個線程擁有。當(dāng)線程獲得這就是一個信號,線程開始擁有的所有權(quán),能夠繼續(xù)執(zhí)行。注意到的以及要喚醒的對象就處于換句話說,如果退出的線程沒有執(zhí)行那么只有能夠獲取執(zhí)行的權(quán)限。
Java支持同步機制的是Monitor。Monitor就像是擁有一個特殊房間的建筑,在同一時間里,這間特殊的房間只能被一個線程擁有。
enter the monitor:進入這幢建筑
acquiring the monitor:進入建筑里的特殊房間
owning the monitor:擁有特殊房間的所有權(quán)
releasing the monitor:離開特殊的房間
exiting the monitor:離開這幢建筑
Monitor支持兩種同步機制:
互斥:通過對象鎖,使得多線程處理能互相獨立的處理共享數(shù)據(jù),而不會發(fā)生線程不安全
協(xié)作:通過對象的wait和notify方法實現(xiàn),比如一個讀的線程從緩沖區(qū)讀數(shù)據(jù),另一個線程負責(zé)往緩沖區(qū)寫數(shù)據(jù),如果緩沖區(qū)沒有數(shù)據(jù),則讀線程阻塞,有數(shù)據(jù)時,讀線程就要開始消費
wait-notify又可以稱作’Singal-continue’。當(dāng)線程獲得 notify,這就是一個信號,線程開始擁有 monitor的所有權(quán),能夠 繼續(xù) 執(zhí)行 monitor region。執(zhí)行完之后,此線程釋放monitor,一個等待的線程則會獲得一樣的機會
Monitor的模型如下:
1 表示線程剛到達 monitor region ,即 enter the monitor
2 表示線程獲取 monitor的所有權(quán),即acquiring the monitor
3 表示線程執(zhí)行了 wait,交出所有權(quán),即releasing the monitor
4 表示原來擁有 monitor 的線程執(zhí)行了 notify ,恰好被這個線程獲取所有權(quán)
5 表示線程執(zhí)行完了 monitor region,即exiting the monitor
Monitor特意把等待的線程分成了兩個部分,Entry Set和Wait Set,Entry Set表示線程剛執(zhí)行到 Monitor region,而Wait Set則是由于線程執(zhí)行了wait方法而進入的區(qū)域。注意到Object的 notify 以及 notifyAll 要喚醒的對象就處于 Wait Set,換句話說,如果退出 monitor 的線程沒有執(zhí)行 notify/notifyAll ,那么只有 Entry Set 能夠獲取執(zhí)行的權(quán)限 。如果執(zhí)行了,則Entry Set和Wait Set中所有的線程都會競爭誰最終能夠獲取 monitor 的能力對象鎖一個線程要離開Wait Set,要么是原擁有 monitor 的線程執(zhí)行了 notify/notifyAll,要么是wait的時間到了,JVM 會觸發(fā)一個notify
Java能夠共享的數(shù)據(jù)包括兩部分:
實例對象:存儲在堆中
類實例:存儲在方法區(qū),當(dāng)鎖一個類的時候,實際上就是鎖類的 Class 對象
對于局部變量,他們存儲在棧中,屬于線程私有,不會存在共享一說。
單個線程可以同時鎖住一個對象多次,JVM會記住鎖住的總次數(shù),每一次釋放鎖,總次數(shù)減一,只有在這個次數(shù)變成0的時候,這個鎖才有可能被其它線程持有
同步代碼塊
同步方法
JVM使用的指令為
monitorenter 獲取引用對象的鎖
monitorexit 是否在monitorenter處獲得的對象鎖
同步代碼塊public class SynchronizedTest { private int i=0; public void syn(){ synchronized (this){ i++; } } }
javap -c SynchronizedTest.class 執(zhí)行后對應(yīng)的指令如下
public class main.lockTest.SynchronizedTest { public main.lockTest.SynchronizedTest(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: aload_0 5: iconst_0 6: putfield #2 // Field i:I 9: return public void syn(); Code: 0: aload_0 // aload_0 屬于 aload_ 系列指令的一種。表示獲取一個本地變量的引用,然后放入棧中 1: dup //彈出棧頂?shù)膯巫止?jié),然后入棧兩次,相當(dāng)于拷貝了棧頂元素 2: astore_1 // astore_ 系列指令的一種。從棧頂獲取對象的引用,并存入本地變量 3: monitorenter //獲取引用對象的鎖 4: aload_0 5: dup 6: getfield #2 // Field i:I 從棧中獲取對象的引用,然后得到它的值 9: iconst_1 // iconst_ 的一種,將常量放入棧中 10: iadd // 從操作棧中彈出兩個integer,把他們相加,然后將結(jié)果重新存入棧中 11: putfield #2 // Field i:I 將值存入引用對象 14: aload_1 15: monitorexit // 釋放引用對象的鎖 16: goto 24 // 跳轉(zhuǎn)到下一個指令的位置 19: astore_2 20: aload_1 21: monitorexit 22: aload_2 23: athrow //從操作棧中刪掉對象的引用,并拋出這個對象的異常 24: return //執(zhí)行返回 Exception table: from to target type 4 16 19 any 19 22 19 any }
注意到,如果拋出了異常,也會執(zhí)行 monitorexit 。印證了無論如何,只要離開了monitor region,鎖都會被釋放
同步方法public class SynchronizedTest { private int i=0; public synchronized void syn(int i){ i++; } }
對應(yīng)指令如下
public class main.lockTest.SynchronizedTest { public main.lockTest.SynchronizedTest(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: aload_0 5: iconst_0 6: putfield #2 // Field i:I 9: return public synchronized void syn(int); Code: 0: iinc 1, 1 3: return }
可以看到兩個區(qū)別
在方法上使用 synchronized 沒有用到 monitorenter / monitorexit 指令。這是因為在方法上使用synchronized并不需要一個本地變量槽(slot)來存儲鎖對象
方法上使用沒有創(chuàng)建一個 異常表
synchronize原理
java 虛擬機指令
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/72389.html
摘要:當(dāng)一個線程持有重量級鎖時,另外一個線程就會被直接踢到同步隊列中等待。 java代碼先編譯成字節(jié)碼,字節(jié)碼最后編譯成cpu指令,因此Java的多線程實現(xiàn)最終依賴于jvm和cpu的實現(xiàn) synchronized和volatile 我們先來討論一下volatile關(guān)鍵字的作用以及實現(xiàn)機制,每個線程看到的用volatile修飾的變量的值都是最新的,更深入的解釋就涉及到Java的內(nèi)存模型了,我們...
摘要:基本元素機制需要幾個元素來配合,分別是臨界區(qū)對象及鎖條件變量以及定義在對象上的,操作。這個外部條件在機制中稱為條件變量。提供的機制,其實是,等元素合作形成的,甚至說外部的條件變量也是個組成部分。 monitor的概念 管程,英文是 Monitor,也常被翻譯為監(jiān)視器,monitor 不管是翻譯為管程還是監(jiān)視器,都是比較晦澀的,通過翻譯后的中文,并無法對 monitor 達到一個直觀的描...
摘要:的關(guān)鍵字中的塊使用關(guān)鍵字進行標(biāo)記。由于每個類只有一個類對象存在于中,因此全局同時只有一個線程能夠進入到同一個類的靜態(tài)同步方法中。同步代碼塊使這種期望成為可能。注意同步代碼塊如何在括號中接受一個對象。相同的實例被傳入兩個不同的線程實例中。 Java的synchronized塊標(biāo)記一個方法或一個代碼塊為同步的。synchronized塊能用于防止出現(xiàn)競態(tài)條件。 Java的synchroni...
摘要:運行可運行狀態(tài)的線程獲得了時間片,執(zhí)行程序代碼。阻塞的情況分三種一等待阻塞運行的線程執(zhí)行方法,會把該線程放入等待隊列中。死亡線程方法執(zhí)行結(jié)束,或者因異常退出了方法,則該線程結(jié)束生命周期。死亡的線程不可再次復(fù)生。 系列文章傳送門: Java多線程學(xué)習(xí)(一)Java多線程入門 Java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(1) java多線程學(xué)習(xí)(二)synchronized關(guān)鍵...
摘要:無論是互斥鎖,還是自旋鎖,在任何時刻,最多只能有一個保持者,也就說,在任何時刻最多只能有一個執(zhí)行單元獲得鎖。另外在中引入了自適應(yīng)的自旋鎖。和關(guān)鍵字的總結(jié)推薦一 該文已加入開源文檔:JavaGuide(一份涵蓋大部分Java程序員所需要掌握的核心知識)。地址:https://github.com/Snailclimb... 本文是對 synchronized 關(guān)鍵字使用、底層原理、JD...
閱讀 984·2021-11-23 09:51
閱讀 3470·2021-11-22 12:04
閱讀 2716·2021-11-11 16:55
閱讀 2921·2019-08-30 15:55
閱讀 3222·2019-08-29 14:22
閱讀 3351·2019-08-28 18:06
閱讀 1240·2019-08-26 18:36
閱讀 2126·2019-08-26 12:08