2020-12-30
技術標籤:Java併發程式設計併發
可重入鎖 ReentrantLock 及其他顯式鎖相關問題
1、跟 Synchronized 相比,可重入鎖 ReentrantLock 其實現原理有什麼不同
(1)底層實現
synchronized是JVM層面的鎖,是java關鍵字。而ReentrantLock是API層面的互斥鎖。
(2)是否可以手動釋放
synchronized不需要手動釋放,程式碼執行完之後系統自動讓執行緒釋放對鎖的佔用;
public class testReentrantLock{ private Lock lock = new ReentrantLock(); public void increment() throws Exception { lock.lock(); try { ........... } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
(3)是否可中斷
synchronized是不可中斷的型別,除非加鎖的程式碼中出現異常或正常執行完成。
ReentrantLock是可以中斷的,可以通過 tryLock(long timeout, TimeUnit unit)方法設定超時,或者呼叫lockInterruptibly()方法進行中斷。
原始碼:
tryLock(long timeout, TimeUnit unit)
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); }
lockInterruptibly()
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
(4)是否公平鎖
Synchronized是非公平鎖。
ReentrantLock預設是非公平鎖,但是也可以通過引數設定為公平鎖。通過構造方法傳入boolean型別引數,false預設是非公平鎖,true為公平鎖。
原始碼:
// 預設是非公平鎖
public ReentrantLock() {
sync = new NonfairSync();
}
// 根據傳入Boolean型別決定公平鎖還是非公平鎖
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
(5)鎖是否可以繫結Condition
synchronized不能繫結
ReentrantLock可以繫結,需要結合await() 、signal()、signalAll()等方法進行操作。
await方法類似Object類的wait()方法,阻塞一個執行緒;signal()、signalAll()類似Object類的notify()/notifyAll()喚醒一個執行緒。
每一個Lock可以有任意資料的Condition物件,Condition是與Lock繫結的。Condition的強大之處在於它可以為多個執行緒間建立不同的Condition。
比如,使用Condition實現生產者-消費者模型:
其中一個是生產者,用於將訊息放入緩衝區;消費者從緩衝區取資料。
現在有一個問題,當緩衝區滿了的時候,而此時生產者還要往緩衝區放資料的時候,此時解決辦法就是讓生產者休眠,等消費者從緩衝區取出一個數據之後,再喚醒生產者。
同樣的,當緩衝區已經空了,而消費者還要來取資料時,此時要讓消費者進行休眠,等待生產者放入一個數據之後再喚醒消費者。
class BoundedBuffer {
final Lock lock = new ReentrantLock();//鎖物件
final Condition notFull = lock.newCondition();//寫執行緒條件
final Condition notEmpty = lock.newCondition();//讀執行緒條件
final Object[] items = new Object[100];//快取佇列
int putptr/*寫索引*/, takeptr/*讀索引*/, count/*佇列中存在的資料個數*/;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)//如果佇列滿了
notFull.await();//阻塞寫執行緒
items[putptr] = x;//賦值
if (++putptr == items.length) putptr = 0;//如果寫索引寫到佇列的最後一個位置了,那麼置為0
++count;//個數++
notEmpty.signal();//喚醒讀執行緒
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)//如果佇列為空
notEmpty.await();//阻塞讀執行緒
Object x = items[takeptr];//取值
if (++takeptr == items.length) takeptr = 0;//如果讀索引讀到佇列的最後一個位置了,那麼置為0
--count;//個數--
notFull.signal();//喚醒寫執行緒
return x;
} finally {
lock.unlock();
}
}
}
(6)鎖的物件
synchronized鎖的是物件,鎖是儲存在物件頭裡面的,根據物件頭標識是否有執行緒獲得鎖/爭搶鎖。
ReentrantLock鎖的是執行緒,根據進入的執行緒和int型別的state標識鎖的獲得/爭搶。