天天看點

Java:sychronize和lock

1.鎖類型名稱解析

  • 可重入鎖:在執行對象的同步方法中不用再次擷取鎖
  • 可中斷鎖:在等待擷取鎖的過程中可以中斷
  • 公平鎖:以每個線程在擷取鎖的等待時間為憑證,等待時間長的在擷取鎖上具有優先權
  • 讀寫鎖:讀資料的時候多條線程不做同步,寫的時候必須同步

2.Synchronized實作原理(同步代碼塊)

核心: Synchronized可以把任何一個非null對象作為"鎖",在HotSpot JVM實作中,鎖有個專門的名字:對象螢幕(Object Monitor)

一個簡單的加了Synchronized的Demo的編譯過程:

public class SynchronizedDemo {
    public void method() {
        synchronized (this) {
            System.out.println("...");
        }
    }
}
           
Java:sychronize和lock
monitorenter:

每個對象都是一個螢幕鎖(monitor)。當monitor被占用時就會處于鎖定狀态,線程執行monitorenter指令時嘗試擷取monitor的所有權

  1. 如果monitor的進入數為0,則該線程進入monitor,然後将進入數設定為1,該線程即為monitor的所有者
  2. 如果線程已經占有該monitor,隻是重新進入,則進入monitor的進入數加1
  3. 如果其他線程已經占用了monitor,則該線程進入阻塞狀态,直到monitor的進入數為0,再重新嘗試擷取monitor的所有權
monitorexit:

執行monitorexit的線程必須是objectref所對應的monitor的所有者。指令執行時,monitor的進入數減1,如果減1後進入數為0,那線程退出monitor,不再是這個monitor的所有者。其他被這個monitor阻塞的線程可以嘗試去擷取這個 monitor 的所有權

1.monitorexit指令出現了兩次,第1次為同步正常退出釋放鎖;第2次為發生異步退出釋放鎖

綜上所述,Synchronized的語義底層是通過一個monitor的對象來完成,其實wait/notify等方法也依賴于monitor對象,這就是為什麼隻有在同步的塊或者方法中才能調用wait/notify等方法,否則會抛出java.lang.IllegalMonitorStateException的異常的原因

3.Synchronized實作原理(同步方法)

一個簡單的加了Synchronized的Demo的編譯過程:

public class SynchronizedMethod {
    public synchronized void method() {
        System.out.println("Hello World!");
    }
}
           
Java:sychronize和lock

從編譯的結果來看,方法的同步并沒有通過指令 monitorenter 和 monitorexit 來完成(理論上其實也可以通過這兩條指令來實作),不過相對于普通方法,其常量池中多了 ACC_SYNCHRONIZED 标示符。JVM就是根據該标示符來實作方法的同步的,具體過程:

  • 當方法調用時,調用指令将會檢查方法的 ACC_SYNCHRONIZED 通路标志是否被設定,如果設定了,執行線程将先擷取monitor,擷取成功之後才能執行方法體,方法執行完後再釋放monitor。在方法執行期間,其他任何線程都無法再獲得同一個monitor對象

4.Lock實作原理粗解(以ReentrantLock為例)

ReentrantLock把所有Lock接口的操作都委派到一個Sync類上,該類繼承了AbstractQueuedSynchronizer

即,AbstractQueuedSynchronizer會把所有的請求線程構成一個CLH隊列,當一個線程執行完畢(lock.unlock())時會激活自己的後繼節點,但正在執行的線程并不在隊列中,而那些等待執行的線程全部處于阻塞狀态,經過調查線程的顯式阻塞是通過調用LockSupport.park()完成,而LockSupport.park()則調用sun.misc.Unsafe.park()本地方法

5.差別:

synchronized Lock
存在層次 Java的關鍵字(普通方法,靜态方法,代碼塊),在jvm層面上
鎖的釋放 ①同步代碼執行完畢,釋放鎖,②執行異常,JVM釋放鎖 在finally釋放,不然會死鎖
鎖的擷取 一個線程擷取鎖,其他線程進入等待,不管擷取鎖的線程是否阻塞 擷取鎖的線程阻塞後,其他線程會試圖擷取鎖
鎖的狀态 無法判斷 可以判斷
鎖的類型 可重入,不可中斷,非公平 可重入,可判斷,可公平
鎖的性能 少量同步 大量同步