咱們先經過反編譯下面的代碼來看看Synchronized是如何實現對代碼塊進行同步的:java
package com.paddx.test.concurrent; public class SynchronizedDemo { public void method() { synchronized (this) { System.out.println("Method 1 start"); } } }
反編譯結果:this
關於這兩條指令的做用,咱們直接參考JVM規範中描述:線程
monitorenter :code
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.對象
這段話的大概意思爲:blog
每一個對象有一個監視器鎖(monitor)。當monitor被佔用時就會處於鎖定狀態,線程執行monitorenter指令時嘗試獲取monitor的全部權,過程以下:ip
一、若是monitor的進入數爲0,則該線程進入monitor,而後將進入數設置爲1,該線程即爲monitor的全部者。ci
二、若是線程已經佔有該monitor,只是從新進入,則進入monitor的進入數加1.rem
3.若是其餘線程已經佔用了monitor,則該線程進入阻塞狀態,直到monitor的進入數爲0,再從新嘗試獲取monitor的全部權。同步
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.
這段話的大概意思爲:
執行monitorexit的線程必須是objectref所對應的monitor的全部者。 指令執行時,monitor的進入數減1,若是減1後進入數爲0,那線程退出monitor,再也不是這個monitor的全部者。其餘被這個monitor阻塞的線程能夠嘗試去獲取這個 monitor 的全部權。
經過這兩段描述,咱們應該能很清楚的看出Synchronized的實現原理,Synchronized的語義底層是經過一個monitor的對象來完成,其實wait/notify等方法也依賴於monitor對象,這就是爲何只有在同步的塊或者方法中才能調用wait/notify等方法,不然會拋出java.lang.IllegalMonitorStateException的異常的緣由。
另一種方法添加Synchronized實現源代碼:
package com.paddx.test.concurrent; public class SynchronizedMethod { public synchronized void method() { System.out.println("Hello World!"); } }
反編譯結果:
從反編譯的結果來看,方法的同步並無經過指令monitorenter和monitorexit來完成(理論上其實也能夠經過這兩條指令來實現),不過相對於普通方法,其常量池中多了ACC_SYNCHRONIZED標示符。JVM就是根據該標示符來實現方法的同步的:當方法調用時,調用指令將會檢查方法的 ACC_SYNCHRONIZED 訪問標誌是否被設置,若是設置了,執行線程將先獲取monitor,獲取成功以後才能執行方法體,方法執行完後再釋放monitor。在方法執行期間,其餘任何線程都沒法再得到同一個monitor對象。 其實本質上沒有區別,只是方法的同步是一種隱式的方式來實現,無需經過字節碼來完成。