Java 并发编程:Synchronized 及其实现原理
作者:liuxiaopeng
原文鏈接:
www.cnblogs.com/paddix/p/5367116.html
一、Synchronized的基本使用
Synchronized是Java中解決并發(fā)問題的一種最常用的方法,也是最簡單的一種方法。Synchronized的作用主要有三個:(1)確保線程互斥的訪問同步代碼(2)保證共享變量的修改能夠及時可見(3)有效解決重排序問題。從語法上講,Synchronized總共有三種用法:
修飾普通方法
修飾靜態(tài)方法
修飾代碼塊
接下來我就通過幾個例子程序來說明一下這三種使用方式(為了便于比較,三段代碼除了Synchronized的使用方式不同以外,其他基本保持一致)。
1、沒有同步的情況:
代碼段一:
執(zhí)行結(jié)果如下,線程1和線程2同時進入執(zhí)行狀態(tài),線程2執(zhí)行速度比線程1快,所以線程2先執(zhí)行完成,這個過程中線程1和線程2是同時執(zhí)行的。
2、對普通方法同步:
代碼段二:
執(zhí)行結(jié)果如下,跟代碼段一比較,可以很明顯的看出,線程2需要等待線程1的method1執(zhí)行完成才能開始執(zhí)行method2方法。
3、靜態(tài)方法(類)同步
代碼段三:
執(zhí)行結(jié)果如下,對靜態(tài)方法的同步本質(zhì)上是對類的同步(靜態(tài)方法本質(zhì)上是屬于類的方法,而不是對象上的方法),所以即使test和test2屬于不同的對象,但是它們都屬于SynchronizedTest類的實例,所以也只能順序的執(zhí)行method1和method2,不能并發(fā)執(zhí)行。
4、代碼塊同步
代碼段四:
執(zhí)行結(jié)果如下,雖然線程1和線程2都進入了對應(yīng)的方法開始執(zhí)行,但是線程2在進入同步塊之前,需要等待線程1中同步塊執(zhí)行完成。
二、Synchronized 原理
如果對上面的執(zhí)行結(jié)果還有疑問,也先不用急,我們先來了解Synchronized的原理,再回頭上面的問題就一目了然了。我們先通過反編譯下面的代碼來看看Synchronized是如何實現(xiàn)對代碼塊進行同步的:
反編譯結(jié)果:
關(guān)于這兩條指令的作用,我們直接參考JVM規(guī)范中描述:
monitorenter :
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.
這段話的大概意思為:
每個對象有一個監(jiān)視器鎖(monitor)。當monitor被占用時就會處于鎖定狀態(tài),線程執(zhí)行monitorenter指令時嘗試獲取monitor的所有權(quán),過程如下:
如果monitor的進入數(shù)為0,則該線程進入monitor,然后將進入數(shù)設(shè)置為1,該線程即為monitor的所有者。
如果線程已經(jīng)占有該monitor,只是重新進入,則進入monitor的進入數(shù)加1.
如果其他線程已經(jīng)占用了monitor,則該線程進入阻塞狀態(tài),直到monitor的進入數(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.
這段話的大概意思為:
執(zhí)行monitorexit的線程必須是objectref所對應(yīng)的monitor的所有者。
指令執(zhí)行時,monitor的進入數(shù)減1,如果減1后進入數(shù)為0,那線程退出monitor,不再是這個monitor的所有者。其他被這個monitor阻塞的線程可以嘗試去獲取這個 monitor 的所有權(quán)。?
通過這兩段描述,我們應(yīng)該能很清楚的看出Synchronized的實現(xiàn)原理,Synchronized的語義底層是通過一個monitor的對象來完成,其實wait/notify等方法也依賴于monitor對象,這就是為什么只有在同步的塊或者方法中才能調(diào)用wait/notify等方法,否則會拋出java.lang.IllegalMonitorStateException的異常的原因。
我們再來看一下同步方法的反編譯結(jié)果:
源代碼:
反編譯結(jié)果:
從反編譯的結(jié)果來看,方法的同步并沒有通過指令monitorenter和monitorexit來完成(理論上其實也可以通過這兩條指令來實現(xiàn)),不過相對于普通方法,其常量池中多了ACC_SYNCHRONIZED標示符。JVM就是根據(jù)該標示符來實現(xiàn)方法的同步的:當方法調(diào)用時,調(diào)用指令將會檢查方法的 ACC_SYNCHRONIZED 訪問標志是否被設(shè)置,如果設(shè)置了,執(zhí)行線程將先獲取monitor,獲取成功之后才能執(zhí)行方法體,方法執(zhí)行完后再釋放monitor。在方法執(zhí)行期間,其他任何線程都無法再獲得同一個monitor對象。 其實本質(zhì)上沒有區(qū)別,只是方法的同步是一種隱式的方式來實現(xiàn),無需通過字節(jié)碼來完成。
三、運行結(jié)果解釋
有了對Synchronized原理的認識,再來看上面的程序就可以迎刃而解了。
1、代碼段2結(jié)果:
雖然method1和method2是不同的方法,但是這兩個方法都進行了同步,并且是通過同一個對象去調(diào)用的,所以調(diào)用之前都需要先去競爭同一個對象上的鎖(monitor),也就只能互斥的獲取到鎖,因此,method1和method2只能順序的執(zhí)行。
2、代碼段3結(jié)果:
雖然test和test2屬于不同對象,但是test和test2屬于同一個類的不同實例,由于method1和method2都屬于靜態(tài)同步方法,所以調(diào)用的時候需要獲取同一個類上monitor(每個類只對應(yīng)一個class對象),所以也只能順序的執(zhí)行。
3、代碼段4結(jié)果:
對于代碼塊的同步實質(zhì)上需要獲取Synchronized關(guān)鍵字后面括號中對象的monitor,由于這段代碼中括號的內(nèi)容都是this,而method1和method2又是通過同一的對象去調(diào)用的,所以進入同步塊之前需要去競爭同一個對象上的鎖,因此只能順序執(zhí)行同步塊。
四、總結(jié)
Synchronized是Java并發(fā)編程中最常用的用于保證線程安全的方式,其使用相對也比較簡單。但是如果能夠深入了解其原理,對監(jiān)視器鎖等底層知識有所了解,一方面可以幫助我們正確的使用Synchronized關(guān)鍵字,另一方面也能夠幫助我們更好的理解并發(fā)編程機制,有助我們在不同的情況下選擇更優(yōu)的并發(fā)策略來完成任務(wù)。對平時遇到的各種并發(fā)問題,也能夠從容的應(yīng)對。
有道無術(shù),術(shù)可成;有術(shù)無道,止于術(shù)
歡迎大家關(guān)注Java之道公眾號
好文章,我在看??
總結(jié)
以上是生活随笔為你收集整理的Java 并发编程:Synchronized 及其实现原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2015年年度总结
- 下一篇: ubuntu14.04下mysql数据库