溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java中同步的實(shí)現(xiàn)原理是什么

發(fā)布時(shí)間:2021-08-06 15:17:01 來源:億速云 閱讀:120 作者:Leah 欄目:編程語(yǔ)言

Java中同步的實(shí)現(xiàn)原理是什么,針對(duì)這個(gè)問題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡(jiǎn)單易行的方法。

Synchronized的基本使用  
 

Synchronized是Java中解決并發(fā)問題的一種最常用的方法,也是最簡(jiǎn)單的一種方法。Synchronized的作用主要有三個(gè):


  1. 確保線程互斥的訪問同步代碼

  2. 保證共享變量的修改能夠及時(shí)可見

  3. 有效解決重排序問題。


從語(yǔ)法上講,Synchronized總共有三種用法:


  1. 修飾普通方法

  2. 修飾靜態(tài)方法

  3. 修飾代碼塊


接下來我就通過幾個(gè)例子程序來說明一下這三種使用方式(為了便于比較,三段代碼除了Synchronized的使用方式不同以外,其他基本保持一致)。


  • 沒有同步的情況


Java中同步的實(shí)現(xiàn)原理是什么


執(zhí)行結(jié)果如下,線程1和線程2同時(shí)進(jìn)入執(zhí)行狀態(tài),線程2執(zhí)行速度比線程1快,所以線程2先執(zhí)行完成,這個(gè)過程中線程1和線程2是同時(shí)執(zhí)行的。


Java中同步的實(shí)現(xiàn)原理是什么

     
  • 對(duì)普通方法同步


Java中同步的實(shí)現(xiàn)原理是什么

     

執(zhí)行結(jié)果如下,跟代碼段一比較,可以很明顯的看出,線程2需要等待線程1的method1執(zhí)行完成才能開始執(zhí)行method2方法。


Java中同步的實(shí)現(xiàn)原理是什么


  • 靜態(tài)方法(類)同步


Java中同步的實(shí)現(xiàn)原理是什么


執(zhí)行結(jié)果如下,對(duì)靜態(tài)方法的同步本質(zhì)上是對(duì)類的同步(靜態(tài)方法本質(zhì)上是屬于類的方法,而不是對(duì)象上的方法),所以即使test和test2屬于不同的對(duì)象,但是它們都屬于SynchronizedTest類的實(shí)例,所以也只能順序的執(zhí)行method1和method2,不能并發(fā)執(zhí)行。


Java中同步的實(shí)現(xiàn)原理是什么


  • 代碼塊同步


Java中同步的實(shí)現(xiàn)原理是什么


執(zhí)行結(jié)果如下,雖然線程1和線程2都進(jìn)入了對(duì)應(yīng)的方法開始執(zhí)行,但是線程2在進(jìn)入同步塊之前,需要等待線程1中同步塊執(zhí)行完成。


Java中同步的實(shí)現(xiàn)原理是什么

     

Synchronized 原理


先通過反編譯下面的代碼來看看Synchronized是如何實(shí)現(xiàn)對(duì)代碼塊進(jìn)行同步的


Java中同步的實(shí)現(xiàn)原理是什么

     

反編譯結(jié)果


Java中同步的實(shí)現(xiàn)原理是什么


關(guān)于這兩條指令的作用,我們直接參考JVM規(guī)范中描述: ` Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows: ? If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor. ? If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count. ? If another thread already owns the monitor associated with objectref, the thread blocks until the monitor’s entry count is zero, then tries again to gain ownership. `


每個(gè)對(duì)象有一個(gè)監(jiān)視器鎖(monitor)。當(dāng)monitor被占用時(shí)就會(huì)處于鎖定狀態(tài),線程執(zhí)行monitorenter指令時(shí)嘗試獲取monitor的所有權(quán),過程如下:


  1. 如果monitor的進(jìn)入數(shù)為0,則該線程進(jìn)入monitor,然后將進(jìn)入數(shù)設(shè)置為1,該線程即為monitor的所有者。


  2. 如果線程已經(jīng)占有該monitor,只是重新進(jìn)入,則進(jìn)入monitor的進(jìn)入數(shù)加1.


  3. 如果其他線程已經(jīng)占用了monitor,則該線程進(jìn)入阻塞狀態(tài),直到monitor的進(jìn)入數(shù)為0,再重新嘗試獲取monitor的所有權(quán)。


monitorexit:  ` The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref. The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.`


  1. 執(zhí)行monitorexit的線程必須是objectref所對(duì)應(yīng)的monitor的所有者。


  2. 指令執(zhí)行時(shí),monitor的進(jìn)入數(shù)減1,如果減1后進(jìn)入數(shù)為0,那線程退出monitor,不再是這個(gè)monitor的所有者。其他被這個(gè)monitor阻塞的線程可以嘗試去獲取這個(gè) monitor 的所有權(quán)。


通過這兩段描述,我們應(yīng)該能很清楚的看出 Synchronized的實(shí)現(xiàn)原理:


Synchronized的語(yǔ)義底層是通過一個(gè)monitor的對(duì)象來完成,其實(shí)wait/notify等方法也依賴于monitor對(duì)象,這就是為什么只有在同步的塊或者方法中才能調(diào)用wait/notify等方法,否則會(huì)拋出java.lang.IllegalMonitorStateException的異常的原因。


我們?cè)賮砜匆幌峦椒椒ǖ姆淳幾g結(jié)果:


Java中同步的實(shí)現(xiàn)原理是什么


 

反編譯結(jié)果


Java中同步的實(shí)現(xiàn)原理是什么


從反編譯的結(jié)果來看,方法的同步并沒有通過指令monitorenter和monitorexit來完成(理論上其實(shí)也可以通過這兩條指令來實(shí)現(xiàn)),不過相對(duì)于普通方法,其常量池中多了ACC_SYNCHRONIZED標(biāo)示符。JVM就是根據(jù)該標(biāo)示符來實(shí)現(xiàn)方法的同步的:當(dāng)方法調(diào)用時(shí),調(diào)用指令將會(huì)檢查方法的 ACC_SYNCHRONIZED 訪問標(biāo)志是否被設(shè)置,如果設(shè)置了,執(zhí)行線程將先獲取monitor,獲取成功之后才能執(zhí)行方法體,方法執(zhí)行完后再釋放monitor。在方法執(zhí)行期間,其他任何線程都無法再獲得同一個(gè)monitor對(duì)象。 其實(shí)本質(zhì)上沒有區(qū)別,只是方法的同步是一種隱式的方式來實(shí)現(xiàn),無需通過字節(jié)碼來完成。


運(yùn)行結(jié)果解釋


有了對(duì)Synchronized原理的認(rèn)識(shí),再來看上面的程序就可以迎刃而解了。


代碼段2結(jié)果: 雖然method1和method2是不同的方法,但是這兩個(gè)方法都進(jìn)行了同步,并且是通過同一個(gè)對(duì)象去調(diào)用的,所以調(diào)用之前都需要先去競(jìng)爭(zhēng)同一個(gè)對(duì)象上的鎖(monitor),也就只能互斥的獲取到鎖,因此,method1和method2只能順序的執(zhí)行。


代碼段3結(jié)果: 雖然test和test2屬于不同對(duì)象,但是test和test2屬于同一個(gè)類的不同實(shí)例,由于method1和method2都屬于靜態(tài)同步方法,所以調(diào)用的時(shí)候需要獲取同一個(gè)類上monitor(每個(gè)類只對(duì)應(yīng)一個(gè)class對(duì)象),所以也只能順序的執(zhí)行。


代碼段4結(jié)果: 對(duì)于代碼塊的同步實(shí)質(zhì)上需要獲取Synchronized關(guān)鍵字后面括號(hào)中對(duì)象的monitor,由于這段代碼中括號(hào)的內(nèi)容都是this,而method1和method2又是通過同一的對(duì)象去調(diào)用的,所以進(jìn)入同步塊之前需要去競(jìng)爭(zhēng)同一個(gè)對(duì)象上的鎖,因此只能順序執(zhí)行同步塊。


關(guān)于Java中同步的實(shí)現(xiàn)原理是什么問題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI