Java併發——鎖框架(一)
來源:《Java執行緒與併發程式設計實踐》以及
1. 鎖框架
java.util.concurrent.locks包提供了一個包含多種介面和類的框架,它針對條件進行加鎖和等待。
2. synchronized關鍵字的缺陷
(1)如果一個程式碼塊或方法被synchronized修飾了,當一個執行緒獲取了對應的鎖,並執行該程式碼塊時,其他執行緒便只能一直等待,等待獲取鎖的執行緒釋放鎖,而這裡獲取鎖的執行緒釋放鎖只會有兩種情況:
1)獲取鎖的執行緒執行完了該程式碼塊,然後執行緒釋放對鎖的佔有;
2)執行緒執行發生異常,此時JVM會讓執行緒自動釋放鎖。
因此就需要有一種機制可以不讓等待的執行緒一直無期限地等待下去(比如只等待一定的時間或者能夠響應中斷),通過Lock就可以辦到。
(2)使用synchronized關鍵字將會隱式地獲取鎖,但是它將鎖的獲取和釋放固化了,也就是先獲取再釋放,這種方式簡化了同步的管理,可是擴充套件性沒有顯式地鎖獲取和釋放來的好。例如,考慮下面這樣一個情景:
針對一個場景,使用synchronized進行鎖獲取和釋放,先獲得鎖A,然後獲得鎖B,當鎖B獲取之後,釋放鎖A同時獲取鎖C,當鎖C獲得後,在釋放B同時獲取鎖D,以此類推。這時,使用synchronized關鍵字就不那麼容易實現了,而使用顯式地鎖獲取和釋放則很簡單。
3. Lock的特點
特 性 | 描述 |
嘗試非阻塞地獲取鎖 | 當前執行緒嘗試獲取鎖,如果這一刻鎖沒有被其他執行緒獲取到,則成功獲取並持有鎖。 |
能被中斷地獲取鎖 | 與synchronized不同,獲取到鎖的執行緒能夠相應中斷,當獲取到鎖的執行緒被中斷時,中斷異常將會被丟擲,同時鎖會被釋放。 |
超時獲取鎖 | 在指定的截止時間之前獲取鎖,如果截止時間到了仍沒有獲取到鎖,則返回。 |
4. Lock介面聲明瞭如下方法:
方法名稱 | 描述 |
void lock() | 獲取鎖,呼叫該方法當前執行緒會獲取鎖,當鎖獲取後,從該方法返回。 |
void lockInterruptibly() throws InterruptedException | 除非呼叫執行緒被中斷,否則獲得鎖。當鎖不可用時,呼叫執行緒被強制一直等待,直到鎖可用或執行緒中斷。 |
boolean tryLock() | 嘗試非阻塞地獲取鎖,呼叫該方法後立即返回,如果能夠獲取鎖則返回true,否則返回false。 |
boolean tryLock(long time, TimeUnit unit) throws InterruptedException | 超時地獲取鎖,當前執行緒在以下3種情況下會返回: 1、 當前執行緒在超時時間內獲取了鎖 2、 當前執行緒在超時時間內被中斷 3、 超時時間結束,返回false |
Condition newCondition() | 獲取等待通知元件,在元件和當前的鎖繫結,當前執行緒只有獲取了鎖,才能呼叫該元件的await()方法,而呼叫後,當前執行緒將會釋放鎖。 |
void unlock() | 釋放鎖 |
5.使用
由於在前面講到如果採用Lock,必須主動去釋放鎖,並且在發生異常時,不會自動釋放鎖。因此一般來說,使用Lock必須在try{}catch{}塊中進行,並且將釋放鎖的操作放在finally塊中進行,以保證鎖一定被被釋放,防止死鎖的發生。通常使用Lock來進行同步的話,是以下面這種形式去使用的:
Lock lock = ...;
lock.lock();
try{
//處理任務
}catch(Exception ex){
}finally{
lock.unlock(); //釋放鎖
}