Lock位於java.util.concurrent.locks包下,是一種線程同步機制,就像synchronized塊一樣。但是,Lock比synchronized塊更靈活、更復雜。
話不多說,我們直接來看官方文檔對Lock接口相關概念及功能的描述,今天又是看英文文檔,翻譯理解的一天。
一、Lock繼承關系

二、官方文檔解讀


三、Lock接口方法解讀
void lock()
獲取鎖。如果鎖不可用,則當前線程將出於線程調度目的而禁用,並處於休眠狀態,直到獲得鎖為止。
void lockInterruptibly() throws InterruptedException;
如果當前線程未被中斷,則獲取鎖。如果鎖可用,則獲取鎖並立即返回。
如果鎖不可用,出於線程調度目的,將禁用當前線程,該線程將一直處於休眠狀態。
下面兩種情形會讓當前線程停止休眠狀態:
-
鎖由當前線程獲取。
-
其他一些線程中斷當前線程,並且支持對鎖獲取的中斷。
當前線程出現下面兩種情況時,將拋出InterruptedException,並清除當前線程的中斷狀態。
-
當前線程在進入此方法時,已經設置為中斷狀態。
-
當前線程在獲取鎖時被中斷,並且支持對鎖獲取中斷。
boolean tryLock();
嘗試獲取鎖,如果鎖處於空閑狀態,則獲取鎖,並立即返回true。如果鎖不可用,則立即返回false。
該方法的典型使用:
Lock lock = ...;
//確保鎖在被獲取時被解鎖
if (lock.tryLock()) {
try {
// manipulate protected state
} finally {
lock.unlock();
}
} else {
// perform alternative actions
}
boolean tryLock(long time, TimeUnit unit) throws
InterruptedException;
該方法為tryLock()的重載方法,兩個參數分別表示為:
- time:等待鎖的最長時間
- unit:時間單位
如果在給定的等待時間內是空閑的並且當前線程沒有被中斷,則獲取鎖。如果鎖可用,則此方法立即獲取鎖並返回true,如果鎖不可用,出於線程調度目的,將禁用當前線程,該線程將一直處於休眠狀態。
下面三種情形會讓當前線程停止休眠狀態:
-
鎖由當前線程獲取。
-
其他一些線程中斷當前線程,並且支持對鎖獲取的中斷。
-
到了指定的等待時間。
當前線程出現下面兩種情況時,將拋出InterruptedException,並清除當前線程的中斷狀態。
-
當前線程在進入此方法時,已經設置為中斷狀態。
-
當前線程在獲取鎖時被中斷,並且支持對鎖獲取中斷。
如果指定的等待時間超時,則返回false值。如果時間小於或等於0,則該方法永遠不會等待。
void unlock()
釋放鎖,與lock()、tryLock()、tryLock(long , TimeUnit)、lockInterruptibly()相對應。
Condition newCondition()
返回綁定到此鎖實例的Condition實例。當前線程只有獲得了鎖,才能調用Condition實例的await()方法,並釋放鎖。
四、重要實現類ReentrantLock
顧名思義,ReentrantLock是重入鎖,關於這個重入鎖,之前涉及過一些知識,在這里做整合,並稍微地補充一下。
ReentrantLock位於java.util.concurrent(J.U.C)包下,是Lock接口的實現類。基本用法與synchronized相似,都具備可重入互斥的特性,但擁有擴展的功能。
RenntrantLock推薦的基本寫法:
class X {
//定義鎖對象
private final ReentrantLock lock = new ReentrantLock();
// ...
//定義需要保證線程安全的方法
public void m() {
//加鎖
lock.lock();
try{
// 保證線程安全的代碼
}
// 使用finally塊保證釋放鎖
finally {
lock.unlock()
}
}
}
1、API層面的鎖
ReentrantLock表現為API層面的互斥鎖,通過lock()和unlock()方法完成,是顯式的,而synchronized表現為原生語法層面的互斥鎖,是隱式的。
2、可重入的
重進入意味着:任意線程在獲取到鎖之后能夠再次獲取該鎖而不會被鎖阻塞,synchronized和Reentrant都是可重入的,隱式顯式之分。
實現可重入需要解決的兩個關鍵部分:
- 鎖需要去識別獲取鎖的線程是否是當前占據鎖的線程,如果是的話,就成功獲取。
- 鎖獲取一次,內部鎖計數器需要加一,釋放一次減一,計數為零表示為成功釋放鎖。
3、可公平的
關於鎖公平的部分,官方文檔是這樣描述的(英文我就不貼了),詞匯較簡單,我試着翻譯一下:
Reentrant類的構造函數接受一個可選的公平性參數fair。這時候就出現兩種選擇:
- 公平的(fair == true):保證等待時間最長的線程優先獲取鎖,即FIFO。
- 非公平的(fair == false):此鎖不保證任何特定的訪問順序。
公平鎖往往體現處的總體吞吐量比非公平鎖要低,也就是更慢。
鎖的公平性並不保證線程調度的公平性,但公平鎖能夠減少"飢餓"發生的概率。
需要注意的是:不定時的tryLock()方法不支持公平性設置。如果鎖可用,即使其他線程等待時間比它長,它也會成功獲得鎖。
4、等待可中斷
當持有線程長期不釋放鎖的時候,正在等待的線程可以選擇放棄等待或處理其他事情。
5、鎖綁定
一個ReentrantLock對象可以通過newCondition()同時綁定多個Condition對象。
JDK1.6之前,ReentrantLock在性能方面是要領先於synchronized鎖的,但是JDK1.6及之后版本實現了各種鎖優化技術,可參考:
聊聊並發Java SE1.6中的Synchronized,后續性能改進會更加偏向於原生的synchronized。
參考資料:
《深入理解Java虛擬機》周志明
《Java並發編程的藝術》方騰飛
