java中鎖的深入理解(一)

JAVA中的鎖分爲兩種,synchronized和Lock。他們都可以實現同步,具體的用法,可以參考我的另一篇文章。



下面深入探討一下,synchronized和Lock兩種鎖方式的原理。


首先看一下synchronized。

public class SynchronizedDemo {
    public void method() {
        synchronized (this) {
            System.out.println("Method 1 start");
        }
    }
}

上面的代碼是一個很簡單的使用synchronized來實現同步的程序。它使用了對象的內部鎖。

下面看一看它的反編譯結果

可以看到,反編譯的結果中,有了monitorenter和monitorexit兩條指令,這兩條指令是JVM指令集中的指令。

下面是monitorenter和moniterexit兩條指令在JVM規範中的描述:

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.
每一個object都與一個監視器相關聯,一個監視器被鎖住當且僅當他有一個使用者。執行moniterenter指令的線程嘗試去獲得與object相關聯監視器的所有權,情況如下:

如果與object相關聯的監視器的進入統計數爲0,那麼線程將進入監視器並將它的進入統計數置爲1,該線程獲得監視器的所有權。

如果線程已經獲得了與該object相關聯的監視器的所有權,那麼它將再次進入,監視器的進入統計數加1。

如果另一個線程已經獲得了與object相關聯的監視器,那麼線程將會被阻塞,直到監視器的進入統計數爲0,然後線程將再次嘗試獲得監視器的所有權。


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的線程必須是與實例關聯的監視器的所有者。線程將監視器的進入統計數減1,如果因此進入統計數的值變爲0,那麼線程將退出該監視器並不再是該監視器的擁有者。其它阻塞的線程被允許再次嘗試進入該監視器。


通過這兩段描述,我們很容易就可以理解synchronized的實現原理,另外,我們還可以注意到,一個線程是可以多次進入一個object的監視器的。

我們再來看一看同步方法:

public class SynchronizedMethod {
    public synchronized void method() {
        System.out.println("Hello World!");
    }
}
上面是一個簡單的同步方法,使用了對象的內部鎖。

我們看一看這段程序反編譯後結果:

我們看見,反編譯後的代碼中有ACC_SYNCHRONIZED標識符。JVM就是根據該標識符來實現方法的同步的:當方法調用時,調用指令將會檢查方法的 ACC_SYNCHRONIZED 訪問標誌是否被設置,如果設置了,執行線程將先獲取monitor,獲取成功之後才能執行方法體,方法執行完後再釋放monitor。在方法執行期間,其他任何線程都無法再獲得同一個monitor對象。 其實本質上沒有區別,只是方法的同步是一種隱式的方式來實現,無需通過字節碼來完成。

總結:

無論學習什麼,底層原理都很重要,既要知其然,也要知其所以然。synchronized的用法雖然簡單,瞭解其底層的原理也很重要。目前,synchronize的效率和lock相比,也還是很不錯的,如果需求簡單的話,更提倡使用synchronized來進行程序設計。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章