synchronized與Lock的區別比較
1、synchronized 是Java內置的關鍵字,使用後會自動釋放鎖,
Lock是java.util.concurrent.Locks 包下的一個接口,必須要手動釋放。特別是在發生異常時,需要在 finally 塊中進行手動釋放,否則會發生死鎖行爲
2、Lock可響應中斷,而synchronized 不能響應中斷,並且Lock提供了更豐富的方法實現;例如
Lock() ; //獲取鎖
tryLock(); //獲取鎖
tryLock(long time, TimeUnit unit); //在一定時間單位內等待
lockInterruptibly(); //獲取鎖,可響應中斷(AB線程同時獲取鎖,A得到後,B進行等待,則B會被Tread.interrupt()方法中端並可去執行其他的代碼邏輯,而synchronized無法被中端)
unlock(); //釋放鎖
3、synchronized 是非公平鎖,即不能保證等待鎖線程的順序,
Lock的實現 ReentrantLock 可通過實例化true or false 的構造參數實現公平鎖和非公平鎖,默認爲非公平鎖
Lock lock = new ReentrantLock();// 非公平鎖
Lock lock2 = new ReentrantLock(true);// 公平鎖
Lock lock3 = new ReentrantLock(false);// 非公平鎖
4、ReentrantLock是唯一實現了Lock接口的類,並且ReentrantLock提供了更多的方法
5、synchronized無法判斷是否獲取鎖的狀態,Lock可以判斷是否獲取到鎖;
6、Lock鎖適合大量同步的代碼的同步問題,synchronized鎖適合代碼少量的同步問題。
7、都是可重入鎖:在執行對象中所有同步方法不用再次獲得鎖
8、synchronized是一個悲觀鎖,Lock是一個樂觀鎖(底層基於volatile和cas實現)
下面奉上一張表格:
類別 | syncronized | Lock |
---|---|---|
存在層次 | Java的關鍵字,在jvm層面上 | 是一個接口 |
鎖的釋放 | 1、以獲取鎖的線程執行完同步代碼,釋放鎖 2、線程執行發生異常,jvm會讓線程釋放鎖 |
在finally中必須釋放鎖,不然容易造成線程死鎖 |
鎖的獲取 | 假設A線程獲得鎖,B線程等待。如果A線程阻塞,B線程會一直等待 | 分情況而定,Lock有多個鎖獲取的方式,具體下面會說道,大致就是可以嘗試獲得鎖,線程可以不用一直等待 |
鎖狀態 | 無法判斷 | 可以判斷 |
鎖類型 | 可重入 不可中斷 非公平 | 可重入 可判斷 可公平(兩者皆可,默認非公平) |
性能 | 少量同步 | 大量同步 |
二、底層實現
1、synchronznized映射成字節碼指令就是增加兩個指令:monitorenter、monitorexit,
當一條線程執行時遇到monitorenter指令時,它會嘗試去獲得鎖,如果獲得鎖,那麼所計數器+1(爲什麼要加1,因爲它是可重入鎖,可根據這個瑣計數器判斷鎖狀態),如果沒有獲得鎖,那麼阻塞,
當它遇到一個monitoerexit時,瑣計數器會-1,當計數器爲0時,就釋放鎖
(tips:節碼中出現的兩個monitoerexit指令的原因是:一個正常執行-1,令一個異常時執行,這兩個用goto的方式只執行一個)
2、Lock底層則基於volatile和cas實現