
Java技術棧
www.javastack.cn
優秀的Java技術公衆号
相信大家在工作或者面試過程中經常聽到重入鎖這個概念,或者與關鍵字 synchrozied 的對比,棧長面試了這麼多人,80%的面試者都沒有答對或沒有答到點上,或者把雙重效驗鎖搞混了,哭笑不得。。
那麼你對重入鎖了解有多少呢?今天,棧長幫大家撕開重入鎖的面紗,來見識下重入鎖的真實容顔。。
什麼是重入鎖
java.util.concurrent.locks.ReentrantLock
這個是 JDK @since 1.5 添加的一種顆粒度更小的鎖,它完全可以替代 synchronized 關鍵字來實作它的所有功能,而且 ReentrantLock 鎖的靈活度要遠遠大于 synchronized 關鍵字。
從類結構圖看出,ReentrantLock 實作了 Lock 接口,ReentrantLock 隻是 Lock 接口的一個實作而已。
java.util.concurrent.locks.Lock
它們都是 java.util.concurrent 包裡面的内容(俗稱 JUC、并發包),也都是 JDK 1.5 開始加入的。
為什麼叫重入鎖呢?
ReentrantLock,我們把它拆開來看就明了了。
Re-Entrant-Lock:即表示可重新反複進入的鎖,但僅限于目前線程;
public void m() { lock.lock(); lock.lock(); try { // ... method body } finally { lock.unlock() lock.unlock() }} lock.lock(); lock.lock(); try { // ... method body } finally { lock.unlock() lock.unlock() }}
如示例代碼所示,目前線程可以反複加鎖,但也需要釋放同樣加鎖次數的鎖,即重入了多少次,就要釋放多少次,不然也會導入鎖不被釋放。
試想一下,如果不設計成可重入鎖,那自己如果反複給自己加鎖,不是會把自己加死鎖了嗎?是以,到現在,重入鎖的概念大概應該清楚了吧?
重入鎖最重要的幾個方法
這幾個方法都是 Lock 接口中定義的:
1)lock()
擷取鎖,有以下三種情況:
2)lockInterruptibly()
擷取鎖,邏輯和 lock() 方法一樣,但這個方法在擷取鎖過程中能響應中斷。
3)tryLock()
從關鍵字字面了解,這是在嘗試擷取鎖,擷取成功傳回:true,擷取失敗傳回:false, 這個方法不會等待,有以下三種情況:
4)tryLock(long timeout, TimeUnit unit)
邏輯和 tryLock() 差不多,隻是這個方法是帶時間的。
5)unlock()
釋放鎖,每次鎖持有者數量遞減 1,直到 0 為止。是以,現在知道為什麼 lock 多少次,就要對應 unlock 多少次了吧。
6)newCondition
傳回一個這個鎖的 Condition 執行個體,可以實作 synchronized 關鍵字類似 wait/ notify 實作多線程通信的功能,不過這個比 wait/ notify 要更靈活,更強大!
重入鎖大概的用法
class X { private final ReentrantLock lock = new ReentrantLock(); // ... public void m() { lock.lock(); // block until condition holds try { // ... method body } finally { lock.unlock() } }}}X { private final ReentrantLock lock = new ReentrantLock(); // ... public void m() { lock.lock(); // block until condition holds try { // ... method body } finally { lock.unlock() } }}}
看見沒有,加鎖和釋放鎖都在方法裡面進行,可以自由控制,比 synchronized 更靈活,更友善。但要注意的是,釋放鎖操作必須在 finally 裡面,不然如果出現異常導緻鎖不能被正常釋放,進而會卡死後續所有通路該鎖的線程。
synchronized 是重入鎖嗎?
那麼問題來了,synchronized 是重入鎖嗎?
你可能會說不是,因為 ReentrantLock 既然是重入鎖,根據推理,相反,那 synchronized 肯定就不是重入鎖,那你就錯了。
答案是:yes,為什麼?看下面的例子:
public synchronized void operation(){ add();}public synchronized void add(){} add();}public synchronized void add(){}
operation 方法調用了 add 方法,兩個方法都是用 synchronized 修飾的,add() 方法可以成功擷取目前線程 operation() 方法已經擷取到的鎖,說明 synchronized 就是可重入鎖。
面試常問的Synchronized的幾種用法推薦看下這篇文章:Synchronized 有幾種用法?。
總結
今天,重入鎖就大概寫到這裡了,其實重入鎖就是一種顆粒度更小的鎖,控制更友善,更強大,棧長隻是簡單介紹一下重入鎖的基本概念及用法,但遠不止這麼簡單,還有很多,一篇也難也詳盡,夠寫好多篇了。
大家也可以關注微信公衆号:Java技術棧,棧長将繼續分享更多重入鎖的進階的概念及工作中的實戰用法,請關注後續文章,或者在公衆号背景回複:多線程,棧長已經整理好了許多 Java 多線程系列文章,都是接地氣幹貨。
覺得有用,轉發分享下朋友圈給更多的人看吧~