一文徹底理解ReentrantLock可重入鎖的使用


java除了使用關鍵字synchronized外,還可以使用ReentrantLock實現獨占鎖的功能。而且ReentrantLock相比synchronized而言功能更加豐富,使用起來更為靈活,也更適合復雜的並發場景。這篇文章主要是從使用的角度來分析一下ReentrantLock。

一、簡介

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

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

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

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

ReentrantLock好像比synchronized關鍵字沒好太多,我們再去看看synchronized所沒有的,一個最主要的就是ReentrantLock還可以實現公平鎖機制。什么叫公平鎖呢?也就是在鎖上等待時間最長的線程將獲得鎖的使用權。通俗的理解就是誰排隊時間最長誰先執行獲取鎖。

字數寫的多可能大家都會煩,干脆直接上代碼演示。

二、使用

1、簡單使用

我們先給出一個最基礎的使用案例,也就是實現鎖的功能。

public class ReentrantLockTest { private static final Lock lock = new ReentrantLock(); public static void main(String[] args) { new Thread(() -> test(),"線程A").start(); new Thread(() -> test(),"線程B").start(); } public static void test() { try { lock.lock(); System.out.println(Thread.currentThread().getName()+"獲取了鎖"); TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }finally { System.out.println(Thread.currentThread().getName()+"釋放了鎖"); lock.unlock(); } } } 

在這里我們定義了一個ReentrantLock,然后再test方法中分別lock和unlock,運行一邊就可以實現我們的功能。這就是最簡單的功能實現,代碼很簡單。我們再看看ReentrantLock和synchronized不一樣的地方,那就是公平鎖的實現。

2、公平鎖實現

對於公平鎖的實現,就要結合着我們的可重入性質了。公平鎖的含義我們上面已經說了,就是誰等的時間最長,誰就先獲取鎖。

public class ReentrantLockTest2 { private static final Lock lock = new ReentrantLock(true); public static void main(String[] args) { new Thread(() -> test(),"線程A").start(); new Thread(() -> test(),"線程B").start(); new Thread(() -> test(),"線程C").start(); new Thread(() -> test(),"線程D").start(); new Thread(() -> test(),"線程E").start(); } public static void test() { for(int i=0;i<2;i++) { try { lock.lock(); System.out.println(Thread.currentThread().getName()+"獲取了鎖"); TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } } } } 

首先new一個ReentrantLock的時候參數為true,表明實現公平鎖機制。在這里我們多定義幾個線程ABCDE,然后再test方法中循環執行了兩次加鎖和解鎖的過程。

 
image

3、非公平鎖實現

非公平鎖那就隨機的獲取,誰運氣好,cpu時間片輪到哪個線程,哪個線程就能獲取鎖,和上面公平鎖的區別很簡單,就在於先new一個ReentrantLock的時候參數為false,當然我們也可以不寫,默認就是false。直接測試一下

 
image

4、響應中斷

響應中斷就是一個線程獲取不到鎖,不會傻傻的一直等下去,ReentrantLock會給與一個中斷回應。在這里我們舉一個死鎖的案例。

首先我們定義一個測試類ReentrantLockTest3。

public class ReentrantLockTest3 { static Lock lock1 = new ReentrantLock(); static Lock lock2 = new ReentrantLock(); public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new ThreadDemo(lock1, lock2)); Thread thread1 = new Thread(new ThreadDemo(lock2, lock1)); thread.start(); thread1.start(); thread.interrupt();//是第一個線程中斷 } } 

在這里我們定義了兩個鎖lock1和lock2。然后使用兩個線程thread和thread1構造死鎖場景。正常情況下,這兩個線程相互等待獲取資源而處於死循環狀態。但是我們此時thread中斷,另外一個線程就可以獲取資源,正常的執行了。

  static class ThreadDemo implements Runnable { Lock firstLock; Lock secondLock; public ThreadDemo(Lock firstLock, Lock secondLock) { this.firstLock = firstLock; this.secondLock = secondLock; } @Override public void run() { try { firstLock.lockInterruptibly(); TimeUnit.MILLISECONDS.sleep(50); secondLock.lockInterruptibly(); } catch (InterruptedException e) { e.printStackTrace(); } finally { firstLock.unlock(); secondLock.unlock(); System.out.println(Thread.currentThread().getName() +"獲取到了資源,正常結束!"); } } } 

我們運行測試一下:

 
image

5、限時等待

這個是什么意思呢?也就是通過我們的tryLock方法來實現,可以選擇傳入時間參數,表示等待指定的時間,無參則表示立即返回鎖申請的結果:true表示獲取鎖成功,false表示獲取鎖失敗。我們可以將這種方法用來解決死鎖問題。

首先還是測試代碼,不過在這里我們不需要再去中斷其中的線程了,我們直接看線程類是如何實現的。

    static class ThreadDemo implements Runnable { Lock firstLock; Lock secondLock; public ThreadDemo(Lock firstLock, Lock secondLock) { this.firstLock = firstLock; this.secondLock = secondLock; } @Override public void run() { try { if(!lock1.tryLock()) {TimeUnit.MILLISECONDS.sleep(10);} if(!lock2.tryLock()) {TimeUnit.MILLISECONDS.sleep(10);} } catch (InterruptedException e) { e.printStackTrace(); } finally { firstLock.unlock(); secondLock.unlock(); System.out.println(Thread.currentThread().getName()+"正常結束!"); } } } 

在這個案例中,一個線程獲取lock1時候第一次失敗,那就等10毫秒之后第二次獲取,就這樣一直不停的調試,一直等到獲取到相應的資源為止。

當然,我們可以設置tryLock的超時等待時間tryLock(long timeout,TimeUnit unit),也就是說一個線程在指定的時間內沒有獲取鎖,那就會返回false,就可以再去做其他事了。

OK,到這里我們就把ReentrantLock常見的方法說明了,所以其原理,還是主要通過源碼來解釋。而且分析起來還需要集合AQS和CAS機制來分析。

 

zz:https://www.jianshu.com/p/80d181a01797



免責聲明!

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



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