ReentrantLock
重入鎖, 表示該鎖支持一個線程對資源的重復(fù)加鎖
類結(jié)構(gòu)
首先讓我們先看下 ReentrantLock 的類結(jié)構(gòu)如下圖所示:
image
從圖中我們可以看出 ReentrantLock 實(shí)現(xiàn) Lock 接口饰剥,同時內(nèi)部類 Sync 是 AQS 的子類蠢沿;而 Sync 又有兩個子類 NonfairSync 和 FairSync 分別對應(yīng)非公平和公平鎖兩種策略搜贤。
構(gòu)造
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
ReentrantLock 默認(rèn)采用非公平的策略跟匆,也可以在構(gòu)造的時候指定是否公平的策略。
非公平鎖
非公平鎖是指在競爭獲取鎖的過程中兔仰,有可能后來者居上
lock() 獲取鎖
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
// CAS 設(shè)置 state 值為 1
if (compareAndSetState(0, 1))
// CAS 成功則說明獲取到鎖, 此時將當(dāng)前線程設(shè)置為獨(dú)占模式下鎖對象的持有者
setExclusiveOwnerThread(Thread.currentThread());
else
// CAS 失敗
// 可能是同一線程再次獲取鎖
// 也可能是不同線程獲取鎖
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
// 調(diào)用父類 sync
return nonfairTryAcquire(acquires);
}
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 此時說明已有線程釋放了鎖
// 有可能是同步隊列里阻塞的線程被喚醒時嘗試獲取鎖
if (compareAndSetState(0, acquires)) {
// CAS 成功則說明獲取到鎖, 此時將當(dāng)前線程設(shè)置為獨(dú)占模式下鎖對象的持有者
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
// 說明同一線程再次獲取鎖
// state 加 1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
獲取鎖的過程如下 :
- 通過 CAS 操作最疆, 設(shè)置 state = 1
- 若 CAS 操作成功轧铁,則將當(dāng)前線程設(shè)置為獨(dú)占模式鎖對象的持有者
- 若 CAS 操作失敗, 最終會調(diào)用 sync 的方法 nonfairTryAcquire; 此時說明可能是同一線程再次嘗試獲取鎖,也有可能是其他線程嘗試獲取鎖
- 若當(dāng)前 state == 0, 繼續(xù)執(zhí)行前兩步操作
- 若當(dāng)前 state != 0, 則判斷當(dāng)前線程是否為鎖的持有者泣矛;若判斷成立疲眷,則對 state + 1
unlock() - 釋放鎖
非公平鎖的釋放調(diào)用的是父類 sync 的 tryRelease 方法
protected final boolean tryRelease(int releases) {
// state 減一操作
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
// 當(dāng)前線程不是當(dāng)前鎖的持有者時拋出異常
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
// 只有 state == 0 時 才是真正完成鎖的釋放
free = true;
// 將鎖的持有者清空
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
從釋放鎖的實(shí)現(xiàn)可以看出,獲取鎖與釋放鎖的操作是對等的乳蓄,譬如下方偽代碼:
ReentrantLock lock = new ReentrantLock();
public void do () {
lock.lock();
try {
do();
// 退出遞歸
} finally {
lock.unlock();
}
}
公平鎖
公平鎖是指獲取鎖的順序完全符合請求時間的順序咪橙,也就是先到先得
lock() - 獲取鎖
接下來我們下公平鎖與非公平鎖在獲取鎖時有什么不同
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 不同于非公平鎖操作,公平鎖多了個判斷條件 hasQueuedPredecessors
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
// h != t 說明同步隊列已有等待的節(jié)點(diǎn)
// s = h.next == null 這個有點(diǎn)沒明白; head 的后置為空應(yīng)該就是 head == tail 吧
// s.thread != Thread.currentThread 是判斷當(dāng)前線程是不是同步隊列的首個阻塞線程 如果是是允許獲取到鎖的
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
Queries whether any threads have been waiting to acquire longer than the current thread.
hasQueuedPredecessors 方法主要實(shí)現(xiàn)的是查找是否有等待時間超過當(dāng)前線程的其他線程, 公平鎖也就是通過該方法保證獲取鎖的有序性虚倒。
unlock() - 釋放鎖
公平鎖的釋放與非公平鎖的釋放操作一致
小結(jié)
- ReentrantLock 如何實(shí)現(xiàn)可重入 ? (通過判斷當(dāng)前線程是否為當(dāng)前鎖對象的持有者)
- 如何實(shí)現(xiàn)公平鎖 ? (若當(dāng)前同步隊列中有等待的節(jié)點(diǎn)則獲取鎖失敗)
- 非公平鎖和公平鎖對性能有什么影響 ? (公平鎖會造成大量的線程切換美侦,非公平鎖會出現(xiàn)線程“饑餓”現(xiàn)象,但線程切換少提高吞吐量)