詳解synchronized

LiuJian-Android發表於2018-01-08

使用

synchronized是Java中的關鍵字,是一種同步鎖。synchronized是內建的語言實現。它修飾的物件有以下幾種:
1. 修飾一個方法,被修飾的方法稱為同步方法,其作用的範圍是整個方法,作用的物件是呼叫這個方法的物件; 鎖是當前例項物件

 public synchronized void accessVal(int newVal);  
複製程式碼

2. 修飾一個程式碼塊,被修飾的程式碼塊稱為同步語句塊,其作用的範圍是大括號{}括起來的程式碼,作用的物件是呼叫這個程式碼塊的物件; 鎖是 Synchronized 括號裡的物件

    synchronized(syncObject) {  
    //允許訪問控制的程式碼  
   }  
複製程式碼

其他:
當有一個明確的物件作為鎖時,就可以這樣寫程式,但當沒有明確的物件作為鎖,只是想讓一段程式碼同步時,可以建立一個特殊的instance變數(它得是一個物件)來充當鎖:

class Foo implements Runnable {
    // 特殊的instance變數
    //零長度的byte陣列物件建立起來將比任何物件都經濟――檢視編譯後的位元組碼:
    //生成零長度的byte[]物件只需3條操作碼,而Object lock = new Object()則需要7行操作碼。
    private byte[] lock = new byte[0];
    Public void methodA() {      
        synchronized(lock) { 
    
    }
}   
複製程式碼

3. 修改一個靜態的方法,其作用的範圍是整個靜態方法,作用的物件是這個類的所有物件;鎖是當前類的 Class 物件。
4. 修改一個類,其作用的範圍是synchronized後面括號括起來的部分,作用的物件是這個類的所有物件。

原理

通常說的synchronized在方法或塊上加鎖,這裡的鎖就是物件鎖(當然也可以在類上面),或者叫重量鎖,在JVM中又叫物件監視器(Monitor),就是物件來監視執行緒的互斥。
物件在堆裡的邏輯結構:

詳解synchronized
物件頭的結構:
詳解synchronized
其中Tag的2bit用來顯示鎖型別。通常我們說synchronized的物件鎖,就是這裡Tag=10時的monitor物件,這裡的Monitor address就是這個monitor物件(就是重量鎖)的地址。
加鎖的過程很簡單:在當前執行緒的棧幀(stack frame)中生成一個鎖記錄(lock record),它只是物件頭的一個拷貝。然後把物件頭裡的tag改成00,並把這個棧幀裡的lock record地址放入物件頭裡。若操作成功,那就完成了輕量鎖操作。如果不成功,說明有執行緒在競爭,則需要在當前物件上生成重量鎖來進行多執行緒同步,然後將Tag狀態改為10,並生成Monitor物件(重量鎖物件),物件頭裡也會放入Monitor物件的地址。最後將當前執行緒t排隊佇列中。當synchronized區域長期都由同一個執行緒加鎖、解鎖時,jvm就用偏向鎖來做,它的加鎖解鎖比輕量鎖操作起來指令更加簡化。不過一旦有其他執行緒使用synchronized區域,即使沒有執行緒間競爭,也會把偏向鎖升級為輕量鎖,當然如果發生執行緒競爭就再升級為物件鎖。

synchronized與Lock的區別

  • Lock是一個介面,而synchronized是Java中的關鍵字,synchronized是內建的語言實現;

  • synchronized在發生異常時,會自動釋放執行緒佔有的鎖,因此不會導致死鎖現象發生;而Lock在發生異常時,如果沒有主動通過unLock()去釋放鎖,則很可能造成死鎖現象,因此使用Lock時需要在finally塊中釋放鎖;

  • Lock可以讓等待鎖的執行緒響應中斷,而synchronized卻不行,使用synchronized時,等待的執行緒會一直等待下去,不能夠響應中斷;

  • 通過Lock可以知道有沒有成功獲取鎖,而synchronized卻無法辦到。

  • Lock可以提高多個執行緒進行讀操作的效率。

  • 在效能上來說,如果競爭資源不激烈,兩者的效能是差不多的,而當競爭資源非常激烈時(即有大量執行緒同時競爭),此時Lock的效能要遠遠優於synchronized。所以說,在具體使用時要根據適當情況選擇。

相關文章