共计 3158 个字符,预计需要花费 8 分钟才能阅读完成。
new ReentrantLock(); 加锁流程:
// 默认执行 NonfairSync.lock(); | |
final void lock() { | |
//cas 0 -> 1 , 如果操作胜利,将以后线程设置为独占线程 | |
if (compareAndSetState(0, 1)) | |
setExclusiveOwnerThread(Thread.currentThread()); | |
else | |
// 如果操作失败 | |
acquire(1); | |
} |
public final void acquire(int arg) {//tryAcquire(arg) 尝试获取锁,如果获取锁失败返回 false, 否则返回 true | |
if (!tryAcquire(arg) && | |
// 尝试取得锁,如果以后线程被中断 返回 true, 否则返回 false | |
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) | |
// 取得锁失败,且以后线程的中断状态为 true, 则从新去尝试 | |
selfInterrupt();} |
tryAcquire() 最终默认调用 ReentrantLock.NonfairSync.nonfairTryAcquire();
final boolean nonfairTryAcquire(int acquires) { | |
// 获取以后线程 | |
final Thread current = Thread.currentThread(); | |
// 获取 state 值 | |
int c = getState(); | |
//0 代表没有线程占用锁 | |
if (c == 0) { | |
//cas 将 0 改为 1 | |
if (compareAndSetState(0, acquires)) { | |
//cas 胜利之后 将独占线程改为以后线程 | |
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"); | |
// 保留新的 state 值 | |
setState(nextc); | |
// 返回 true | |
return true; | |
} | |
// 否则,就返回 false | |
return false; | |
} |
AbstractQueuedSynchronizer.addWaiter(Node.EXCLUSIVE); 解析
private Node addWaiter(Node mode) {Node node = new Node(Thread.currentThread(), mode); | |
// 尾节点赋值给 pred;Node pred = tail; | |
// 尾节点不为空 | |
if (pred != null) { | |
// 以后的节点 prev 指针指向当初的尾节点 | |
node.prev = pred; | |
//cas 将以后尾节点 替换为 node 节点 | |
if (compareAndSetTail(pred, node)) { | |
// 以后尾节点的 next 指针指向 node 节点 | |
pred.next = node; | |
return node; | |
} | |
} | |
// 如果尾节点为空或者 cas 替换失败,则执行入队操作 | |
enq(node); | |
return node; | |
} | |
// 入队操作:将以后节点设置为尾节点,并更新以后节点和替换前的尾节点的指针指向 | |
private Node enq(final Node node) {for (;;) { | |
// 把尾节点赋值给一个变量 | |
Node t = tail; | |
// 如果尾节点为空 | |
if (t == null) { | |
// 新建一个节点,设置为头节点 | |
if (compareAndSetHead(new Node())) | |
// 把头节点赋值尾节点 | |
tail = head; | |
} else { | |
// 如果尾节点不为空,以后节点的 prev 指针指向尾节点 | |
node.prev = t; | |
//cas 将以后节点设置为尾节点 | |
if (compareAndSetTail(t, node)) { | |
// 替换前的尾节点的 next 指针指向以后节点 | |
t.next = node; | |
return t; | |
} | |
} | |
// 如果 cas 替换尾节点 失败,则循环执行,直到胜利为止 | |
} | |
} |
AbstractQueuedSynchronizer.acquireQueued(addWaiter(Node.EXCLUSIVE), arg);
// 曾经入队的非中断线程再次尝试获取锁 | |
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); | |
} | |
} |
shouldParkAfterFailedAcquire(p, node) 细节如下:
// 获取锁失败的线程查看并更新 node 中的 waitStatus。如果线程应该被阻塞返回 true。private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { | |
// 获取前一个节点的期待状态 | |
int ws = pred.waitStatus; | |
//-1,代表前一个节点被阻塞中, 须要唤醒 | |
if (ws == Node.SIGNAL) | |
return true; | |
if (ws > 0) { | |
// > 0 代表前一个节点被勾销,就递归往找 waitStatus > 0 的节点信息 | |
do {node.prev = pred = pred.prev;} while (pred.waitStatus > 0); | |
// 找到后,将此节点的 next 指针指向以后节点 | |
pred.next = node; | |
} else { | |
/* | |
* waitStatus must be 0 or PROPAGATE. Indicate that we | |
* need a signal, but don't park yet. Caller will need to | |
* retry to make sure it cannot acquire before parking. | |
* waitStatus 必须是 0 或者 -3(示意能够共享取得)。表明咱们须要唤醒,然而还没有阻塞。调用者须要重试去保障在阻塞操作之前不能获取胜利 | |
*/ | |
//cas 将 前一个节点的 waitStatus 改为 -1(须要唤醒)compareAndSetWaitStatus(pred, ws, Node.SIGNAL); | |
} | |
// 返回 false | |
return false; | |
} | |
private final boolean parkAndCheckInterrupt() { | |
// 阻塞以后线程 | |
LockSupport.park(this); | |
// 以后线程被唤醒后,判断以后线程的的中断状态 | |
// 这里有一个十分重要的知识点:唤醒阻塞线程的形式 1.unpark 2.interrupt | |
return Thread.interrupted();} |
正文完