Synchronized
我們知道 Synchronized 是 Java 中解決并發(fā)問題的一種最常用的方法, 也是最簡單的一種方法. 被也被稱為內(nèi)置鎖.
Synchronized 的作用主要有三個(gè):
確保線程互斥的訪問同步代碼
保證共享變量的修改能夠及時(shí)可見
有效解決重排序問題。
從語法上講, Synchronized 總共有三種用法:
修飾普通方法, 鎖是當(dāng)前實(shí)例對(duì)象.
修飾靜態(tài)方法, 鎖是當(dāng)前類的 class 對(duì)象.
修飾代碼塊, 鎖是括號(hào)中的對(duì)象.
關(guān)于使用方式, 這里就不再進(jìn)行一一描述了. 我們直接進(jìn)入正題, 看 Synchronized 的底層實(shí)現(xiàn)原理是什么.
1. Synchronized 原理
首先, 我們先來看一段代碼, 使用了同步代碼塊和同步方法, 通過使用 javap 工具查看生成的 class 文件信息來分析 synchronized 關(guān)鍵字的實(shí)現(xiàn)細(xì)節(jié).
代碼片段
public static void main(java.lang.String[]) throws java.lang.Exception; descriptor: ([Ljava/lang/String;)V flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: getstatic 3: dup 4: astore_1 5: monitorenter //---------------------------------------------1. 6: aload_1 7: monitorexit //---------------------------------------------2. 8: goto 16 11: astore_2 12: aload_1 13: monitorexit //---------------------------------------------3. 14: aload_2 15: athrow 16: return ... public static synchronized void test(); descriptor: ()V flags: (0x0029) ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED //---------------------------------------------4. Code: stack=0, locals=0, args_size=0 0: return LineNumberTable: line 21: 0
從生產(chǎn)的 class 信息中, 可以清楚的看到兩部分內(nèi)容
同步代碼塊中使用了 monitorenter 與 monitorexit 指令.
同步方法中依靠方法修飾符 flags 上的 ACC_SYNCHRONIZED 實(shí)現(xiàn).
先看反編譯出 main 方法中標(biāo)記的 1 與 2. monitorenter / monitorexit 關(guān)于這兩條指令的作用, 參考 JVM 中對(duì)他們的描述如下:
monitorenter
每個(gè)對(duì)象有一個(gè)監(jiān)視器鎖 monitor, 當(dāng) monitor 被占用時(shí)就會(huì)處于鎖定狀態(tài), 線程執(zhí)行 monitorenter 指令時(shí)嘗試獲取 monitor 的所有權(quán), 過程如下
如果 monitor 的進(jìn)入數(shù)為 0 , 則該線程進(jìn)入 monitor, 然后將進(jìn)入數(shù)設(shè)置為 1, 該線程即為 monitor 的擁有者.
如果線程已經(jīng)占有該 monitor, 只是重新進(jìn)入, 則進(jìn)入 monitor 的進(jìn)入數(shù)加 1.
如果其他線程已經(jīng)占用了 monitor, 則該線程進(jìn)入阻塞狀態(tài), 直到 monitor 的進(jìn)入數(shù)為 0, 再嘗試獲取 monitor 的所有權(quán).
monitorexit
執(zhí)行 monitorexit 的線程必須是對(duì)應(yīng) monitor的所有者. 執(zhí)行指令時(shí), monitor的進(jìn)入數(shù)減 1. 如果減 1 后進(jìn)入數(shù)為 0, 則線程退出 monitor. 不再是這個(gè) monitor 的所有者. 其他被這個(gè) monitor 阻塞的線程可以嘗試去獲取這個(gè) monitor 的所有權(quán).
monitorenter 指令是在編譯后插入到同步代碼塊開始的位置, 而 monitorexit 是插入到方法的結(jié)束處和異常處. 這也就是為什么在 3 處會(huì)單獨(dú)有一個(gè) monitorexit 了.
ACC_SYNCHRONIZED
當(dāng)方法調(diào)用時(shí), 調(diào)用指令將檢查方法的 ACC_SYNCHRONIZED 訪問標(biāo)志是否被設(shè)置, 如果設(shè)置了, 執(zhí)行線程將先獲取 monitor, 獲取成功之后才能執(zhí)行方法體. 方法執(zhí)行完后再釋放 monitor, 在方法執(zhí)行期間, 其他任何線程都無法再獲得同一個(gè) monitor 對(duì)象.
其實(shí)這個(gè)和上面 monitorenter 與 monitorexit 本質(zhì)上沒有區(qū)別, 只是方法的同步是一種隱式的方式來實(shí)現(xiàn)的, 無需通過字節(jié)碼來完成.
看完這些, 是不是覺得有點(diǎn)和 AQS 中的 state 相似? 如果看完了 從 LockSupport 到 AQS 的簡單學(xué)習(xí) 這篇文章的朋友, 再來看這里, 我相信應(yīng)該會(huì)很容易理解.
任務(wù)調(diào)度
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。