關於ReentrantLock鎖的一些理解


 簡介

ReentrantLock常常對比着synchronized來分析,我們先對比着來看然后再一點一點分析。

(1)synchronized是獨占鎖,加鎖和解鎖的過程自動進行,易於操作,但不夠靈活。ReentrantLock也是獨占鎖,加鎖和解鎖的過程需要手動進行,不易操作,但非常靈活。

(2)synchronized可重入,因為加鎖和解鎖自動進行,不必擔心最后是否釋放鎖;ReentrantLock也可重入,但加鎖和解鎖需要手動進行,且次數需一樣,否則其他線程無法獲得鎖。

(3)synchronized不可響應中斷,一個線程獲取不到鎖就一直等着;ReentrantLock可以相應中斷。

 

場景1:如果發現該操作已經在執行中則不再執行(有狀態執行)

a、用在定時任務時,如果任務執行時間可能超過下次計划執行時間,確保該有狀態任務只有一個正在執行,忽略重復觸發。
b、用在界面交互時點擊執行較長時間請求操作時,防止多次點擊導致后台重復執行(忽略重復觸發)。

以上兩種情況多用於進行非重要任務防止重復執行,(如:清除無用臨時文件,檢查某些資源的可用性,數據備份操作等)

這里寫圖片描述

 

場景2:如果發現該操作已經在執行,等待一個一個執行(同步執行,類似synchronized)

這種比較常見大家也都在用,主要是防止資源使用沖突,保證同一時間內只有一個操作可以使用該資源。
但與synchronized的明顯區別是性能優勢(伴隨jvm的優化這個差距在減小)。同時Lock有更靈活的鎖定方式,公平鎖與不公平鎖

 

ReentrantLock默認情況下為不公平鎖

不公平鎖與公平鎖的區別:
公平情況下,操作會排一個隊按順序執行,來保證執行順序。(會消耗更多的時間來排隊)
不公平情況下,是無序狀態允許插隊,jvm會自動計算如何處理更快速來調度插隊。(如果不關心順序,這個速度會更快)

這里寫圖片描述

 

場景3:如果發現該操作已經在執行,則嘗試等待一段時間,等待超時則不執行(嘗試等待執行)

這種其實屬於場景2的改進,等待獲得鎖的操作有一個時間的限制,如果超時則放棄執行。
用來防止由於資源處理不當長時間占用導致死鎖情況(大家都在等待資源,導致線程隊列溢出)。

這里寫圖片描述

 

場景4:如果發現該操作已經在執行,等待執行。這時可中斷正在進行的操作立刻釋放鎖繼續下一操作

synchronized與Lock在默認情況下是不會響應中斷(interrupt)操作,會繼續執行完。lockInterruptibly()提供了可中斷鎖來解決此問題。(場景2的另一種改進,沒有超時,只能等待中斷或執行完畢)

這種情況主要用於取消某些操作對資源的占用。如:(取消正在同步運行的操作,來防止不正常操作長時間占用造成的阻塞)

這里寫圖片描述

 

下面是ReentrantLock的一個代碼示例

//: concurrency/AttemptLocking.java // 以下是ReentrantLock中斷機制的一個代碼實現、如果換成synchronized就會出現死鎖 import java.util.concurrent.*; import java.util.concurrent.locks.*; public class AttemptLocking { private ReentrantLock lock = new ReentrantLock(); public void untimed() { boolean captured = lock.tryLock(); try { System.out.println("tryLock(): " + captured); } finally { if (captured) lock.unlock(); } } public void timed() { boolean captured = false; try { captured = lock.tryLock(2, TimeUnit.SECONDS); } catch (InterruptedException e) { throw new RuntimeException(e); } try { System.out.println("tryLock(2, TimeUnit.SECONDS): " + captured); } finally { if (captured) lock.unlock(); } } public static void main(String[] args) throws InterruptedException { final AttemptLocking al = new AttemptLocking(); al.untimed(); // True -- 可以成功獲得鎖 al.timed(); // True --可以成功獲得鎖 //新創建一個線程獲得鎖並且不釋放 new Thread() { { setDaemon(true); } public void run() { al.lock.lock(); System.out.println("acquired"); } }.start(); Thread.sleep(100);// 保證新線程能夠先執行 al.untimed(); // False -- 馬上中斷放棄 al.timed(); // False -- 等兩秒超時后中斷放棄 } } 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM