新手學(xué)習(xí),若有不對,歡迎大佬 調(diào)教??????
ReentrantLock
我們經(jīng)常用的 *ReentrantLock*是干什么的呢 我認(rèn)為這是一個前臺/門面(類似設(shè)計模式中的門面模式)根據(jù)我們的入?yún)?chuàng)建一個FairSync OR NonfairSync 。sync 擔(dān)任鎖的lock()和release()。
private final Sync sync;
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
那有人可能就問了啥是公平鎖(FairSync)? 啥是非公平鎖(NonfairSync)?
就拿商場試吃舉例子,前者就是大家都好好排隊,后者是新來的看試吃小樣還有,直接拿走不參與排隊,那顯然后面的人就會饑餓 啊。那非公平鎖有什么意義呢。想象一下,當(dāng)商場人滿為患了,你去排到試吃的后面都要擠過來,擠過去。顯然你在全局上影響了商場的客流動,如果你直接去 偷襲!(馬保國音) 顯然在商場全局上來說是最優(yōu)的。
加鎖
AQS入隊
因?yàn)?code>FairSync 和NonfairSync 差的不是很大, 我們就著重講NonfairSync
那你說那我缺的這塊FairSync誰給我補(bǔ)啊,想要就自己來拿( 指自己看源碼) 維吉爾音
//java.util.concurrent.locks.ReentrantLock
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
可見如果CAS成功線程就直接獲得鎖了,不成功就走了 acquire() 因?yàn)?code>Sync extends AbstractQueuedSynchronizer讓我們來看看acquire()
// java.util.concurrent.locks.AbstractQueuedSynchronizer
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire() 獲取鎖失敗進(jìn)入AQS等待隊列
AQS終于是露出雞腳了acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
AQS(AbstractQueuedSynchronizer)抽象隊列同步器,名字是不是很高大上,我們別管
就是商場老大爺、老大媽排隊購物(先進(jìn)先出的雙向鏈表)。
讓我們看看node具有的屬性
static final class Node {
// 共有鎖?
static final Node SHARED = new Node();
// 獨(dú)占鎖?
static final Node EXCLUSIVE = null;
// 線程被取消
static final int CANCELLED = 1;
// 線程處于激活態(tài)
static final int SIGNAL = -1;
// 線程在等待中
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3;
讓我們再看看addWaiter() 通過CAS確保成功加入最后一個節(jié)點(diǎn)。
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node); // 對AQS進(jìn)行初始化再加入
return node;
}
enq() 對隊列進(jìn)行初始化,添加一個虛擬節(jié)點(diǎn)(避免空指針)
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
AQS出隊
讓我們回到 acquire()
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
買菜大媽也挺急的,要排隊就會催前面快點(diǎn),于是拍拍前面的人,說往前催一下。(少數(shù)情況)前面的人也很急,看著時間來不及燒菜了,就自暴自棄,直接離開了,空出了位置。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
// 外部中斷,或線程取消等待
} finally {
if (failed)
cancelAcquire(node);
}
}
后面的人看到前面有空位,就往前走再催前面的人。看到前面的人已經(jīng)在催前面的人,他就不催了,催玩之后自己就能待機(jī)了(干著急也沒用)。
為什么會 看到前面的人已經(jīng)在催前面的人 可能有兩個節(jié)點(diǎn)被同時加入
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)// 前面的人已經(jīng)在問了
return true;
if (ws > 0) { // 取消節(jié)點(diǎn),空出位置,往前挪
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
解鎖
我們來看看鎖的釋放隊列隊列為空則調(diào)用unparkSuccessor(h) ,為什么 waitState以等于0做標(biāo)記,且看下文
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0) // 檢查AQS是否初始化,或隊列是否為空
unparkSuccessor(h);
return true;
}
return false;
}
waitState等于0可簡單看做,已經(jīng)完成了他作為解鎖信號的職責(zé),同時這和 -1是不一樣的,
-1 是未知的往前催(不知道前面好沒好),0是肯定的說前面有一個空位,并且是head指針自發(fā)的,不會傳遞。
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0); // 重置 waitStatus為 0
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev) // 如果你觀察到了這段的奇怪之處,我也沒辦法解釋,看了文章也看到不是很明白,就不誤導(dǎo)人了。相關(guān)內(nèi)容在 java.util.concurrent.locks.AbstractQueuedSynchronizer#cancelAcquire
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread); // 喚醒下一個線程
}
隊列被 unpark() 喚醒,隊伍可以向前移動了
如果覺得有幫到你
點(diǎn)個贊再走唄baby ??????
參考文章:
浙公網(wǎng)安備 33010602011771號