JDK1.5開始引入顯式鎖,Lock
與使用synchronized
方法和語句相比,實現提供了更廣泛的鎖定操作。它們允許更靈活的結構,可以具有完全不同的屬性。
1.接口說明
Modifier and Type | Method and Description |
---|---|
void |
lock() Acquires the lock.(獲取鎖) |
void |
lockInterruptibly() Acquires the lock unless the current thread is interrupted.(獲取鎖,除非當前線程被中斷。) |
Condition |
newCondition() Returns a new Condition instance that is bound to this Lock instance.(返回一個Condition實例,java條件變量) |
boolean |
tryLock() Acquires the lock only if it is free at the time of invocation.(嘗試獲取鎖) |
boolean |
tryLock(long time, TimeUnit unit) Acquires the lock if it is free within the given waiting time and the current thread has not been interrupted.(嘗試獲取鎖,超市中斷) |
void |
unlock() Releases the lock.(釋放鎖) |
2.基本使用
public static void main(String[] args) {
Lock lock = new ReentrantLock();
//申請鎖
lock.lock();
try {
//todo 業務邏輯
}finally {
// unlock放到finally塊中,避免鎖泄露
lock.unlock();
}
}
- lock/unlock是成對使用的,unlock要放到finally中,避免鎖泄露
- ReentrantLock是可重入鎖,new ReentrantLock(boolean fair)當fair爲true表示公平鎖,因爲公平鎖使用開銷比非公平鎖打,所以默認爲非公平鎖策略。
3.Lock和synchronized對比
-
synchronized比較死板,在申請鎖時只能等待,不能中斷,不能跨越方法塊,Lock相對靈活,有tryLock()方法,這樣就有了等待超時,而且可以在一個方法中申請,另一個方法中釋放,比較靈活。
-
synchronized不容易有鎖泄露,在使用Lock的時候稍微不注意就會造成鎖泄露。切記lock/unlock要成對出現,unlock要放到finally中。
-
synchronized具有可重入性,但是非公平,Lock同樣具有重入性,但是也有公平鎖。
-
ReentrantLock還有很多方法
- isLocked()方法用於檢測鎖是否被某個線程持有。
- getQueueLength()方法檢測等待線程的數量。
- hasQueuedThreads()是否有線程等待
- hasQueuedThread(Thread thread)當前線程是否在等待
- isFair()是否是公平鎖,返回true則爲公平鎖
- 等等詳細到->官網地址
4.讀寫鎖
4.1定義
鎖的排他性使得多個線程無法以安全的方式在同一時間對共享變量的進行讀取(僅僅讀取),這個不利於提高系統的併發性。
讀寫鎖是一種改型的排它鎖。
-
它允許多個線程同時讀取(只讀)共享變量,但是隻允許一個線程對線程的共享變量進行更新(包括讀取更新)。
-
任何線程讀取共享變量的時候,其他線程無法更新變量。
-
一個線程更新變量的時候,其他線程無法訪問變量。
-
讀鎖是共享的,可以有多個線程一起持有;寫鎖是排他的,一個線程獲取寫鎖之後,其他線程無法再次獲取寫鎖和讀鎖。
4.2代碼實現
public static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public static final Lock read = lock.readLock();
public static final Lock write = lock.writeLock();
public static void main(String[] args) {
// 讀鎖
read.lock();
try{
//todo
}catch (Exception e){
}finally {
// 在finally中釋放,避免鎖泄露
read.unlock();
}
// 寫鎖
write.lock();
try{
//todo
}catch (Exception e){
}finally {
// 在finally中釋放,避免鎖泄露
write.unlock();
}
}
讀寫鎖和ReentrantLock邏輯類似,畢竟都是繼承了Lock接口,
在讀取操作較多,更新操作較少的場景用讀寫鎖能更好的優化系統的性能。
4.3讀寫鎖降級
ReentrantReadWriteLock支持鎖降級,也就是在獲取寫鎖的情況下可以獲取讀鎖。
注意:ReentrantReadWriteLock 不支持鎖升級,讀鎖想要獲取寫鎖,必須先釋放讀鎖,再申請寫鎖。