这次补全上次 Lock 留下的坑,把剩余的源码弥补完事。

AQS 开释锁逻辑

release(int arg)

开释锁的调用联络

ReentrantLock.unlock源码怎样做成app软件() —> sync.release() —> AQS 提供的 relsese

public void un源码编辑器手机版下载lock() {
sync.release(1)源码本钱;
}
public final boolean re源码年代lease(int arg) {
if (tryRelease(arg)) {
Node源码编辑器 h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryReleas源码本钱e(int releases) {
// 减去开释的值..
int c = ge源码共享网tState() - releases源码之家;
// 假定当时线程并未持锁,直接失常。
if (Thre源码编辑器手机版下载ad.currentThread() != getExclusiveOwnerThr源码年代ead())
throw new IllegalMonitorStateException(源码);
// 当时线程持有锁
boolean free = false;//是否现已完全开释锁
// 只需当时线程的 state 值为 0,才是完全开释锁
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}

unpa源码超市rkSuccessor(Node node)

AQS 中的 unparkSuccessor。

唤醒当时节点的下一个节点。

    private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0) // 改成 0 的原因:由于当时节点现已完毕唤醒后继节点的使命了。
com源码年代pareAndSetWaitStatus(node, ws, 0);
// 当时节点的第一个后继节点
Node s = node.next源码共享网;
/*
*什么时分 s 等于 null 呢
*1.当时节点便是 tail 节点时
*2.当节点未入队完毕时
*  重述一遍入队进程:
*    ① 设置新节点的 prev 指向 pred
*    ② cas 这只新节点为 tail
*    ③ Pred.next 指向新节点(源码编辑器编程猫下载此处未完毕)
*  当 ③ 还未完毕时当时节点调用 release, 那么你拿到的 n源码本钱ode.next 为 Nu源码本钱ll
*需求找到能够唤醒的节点
*/
//源码编辑器编程猫下载接下来看条件二
//假定进入条件源码怎样做成app软件二,那么说明 s != null
//说明源码超市 s 节点为撤消状况,那么需求找一个合适的能够被唤醒的节点
if (s == null || s.waitStatus > 0) {
// 查找能够被唤醒的节点
s = null;
// 从队尾遍历,会找到一个离当时 node 最近的一个能够被唤醒的源码年代 node,
// node 可能找不到,node 可能是 null
for (Node t = tail; t != null &源码年代& t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
// 假定找到合适的能够被唤醒的 node,则唤醒..找不到 什么也源码编辑器手机版下载不做。
if (s != null)
LockSupport.unpark(s.thread);
}

总的来说一下上面三个方法。首要假定咱们调用 ReentrantLock 的 un源码编程lock 方法,实际上咱们便是调用 AQS的 release 方法。在 release 方法里咱们首要会去检源码之家验开释源码编辑器手机版下载锁,什么时分算完全开释锁呢,当时线程的 state = 0的时分。假定当时 s源码怎样做成app软件tate 值为 0,那么假定当时线程不是空并且不是尾节点的话咱们就会查验唤醒后继节点。去堵塞部队里找到距离当时节点源码本钱最近的,并且唤醒 waitStatus 小于等于 0 的节点。以上便是开释锁的大体逻辑。

AQS 呼应间断出队

cancelAcquire(Node node)

撤消指定 n源码怎样做成app软件ode 参加竞赛

private void cancelAcquire(Node node) {
if (node == null)
return;
// 由于现已撤消排队了,所以 node 内部关联的当时线程,置为 NULL 就好了
node.thread = null;
Node pred = node.prev;
w源码之家hile (pred.waitStatus &源码之家gt; 0)
node.prev = pred = pred.prev;
Node predNext = pred.n源码编辑器手机版下载ext;
// 将当时n源码ode 状况设置为源码本钱 撤消状况 1
node.waitStatus源码年代 = Node.CANCELLED;
/*
* 当时撤消排队的node 地址部队的方位不同,实行的出队源码怎样做成app软件战略源码怎样做成app软件是不相同的,一共分为三种状况:
* 1.当时 node 是队尾  tail -> node
* 2.当时 node 不是 he源码本钱ad.next 节点,也不是 tail
* 3.当时 node 是 head.next 节点
*/
//条件一: node == tail  当时 Node 是队尾
//条件二: 成功的话,说明批改 tail 完毕
i源码年代f (node == ta源码编辑器手机版下载il && compare源码编辑器编程猫下载AndSetTai源码超市l(node, pred)) {
// 假定当时node 是队尾的话必定要将 node 与前继节点断开然后尾指针指向前继节点
compar源码编程eAndSetNext(pred, predNext, null);
} else {
int ws;
// 当时 node 不是 head.next 节点,也不是 tail
i源码编辑器编程猫下载f (pred != head &&
// 条件2.1:树立,说明 node 的前驱状况是 signal 状况
//         不树立:前驱状况可能是 0 ,也可能是 1源码本钱(前驱也撤消排队..)
// 条件2.2:假定前驱状况 &l源码之家t;= 0,则设置前驱状况为 Signal 状况源码编程..标明要唤醒后继节点。
//if 里面:便是让 pred.next -> node.next,所以需源码年代求确保 pred 节点状况为 signa源码编程l 状况
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 &&源码编辑器手机版下载amp源码编辑器; c源码本钱ompareAndSetWaitStatus(pred, ws, Node.SIGNAL源码编辑器编程猫下载))) &&
pred.thread != null源码怎样做成app软件) {
// 出队:pred.next -> node.nex源码共享网t 节点后,当 node.next 节点被唤源码本钱醒后
// 调用 shouldParkAfterFailedAcquire 会让 node.next 节点跳过撤消状况的节点
// 完毕真正出队
Node next = node.next;
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
//当时 node 是 head.next 节点
// 后继节点唤醒后,会调用 shouldParkAfterFailedAcquire 会让 node.next 节点跳过撤消状况的节点
// 部队的第三个节点 会 直接与 head 树立 双重指向的联络:head.ne源码编辑器编程猫下载xt -> 第三个 node 中心就源码本钱是被出队                 //的 head.next 第三个 node.prev -> head(下面有简图)
unparkSuccessor(node);
}
// 指向自己,出队
node源码超市.next = node; // help GC
}
}

AQS 非公正锁

顺带说几嘴非公正锁,假定你理解了公正锁,非公正锁只是稍有不同。

static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void loc源码超市k() {
// 上来就查验加锁,也不论部队里有没有其他线程在等候,也不论当时状况有没有加锁。
if (compareAndSetState(0, 1))
// 抢占成功设置独占
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires源码超市) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int a源码之家cquires)源码年代 {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 假定当时状况为0,源码编辑器它仍是不判别当时部队里是否有等候源码年代线程,直接查验抢锁
if (compareAndSetState源码编辑器手机版下载(0, acquires))源码本钱 {
setExclusiveOwnerThread(current源码编辑器编程猫下载);
return true;
}
}
// 没成功查看有没有重入
else if (current == g源码etExclusiveOwnerThread()) {
int nextc =源码编程 c +源码 acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exc源码年代eeded");
setState(nextc);
return true;
}
return false;
}
}
// 接下来都相源码超市同,该入部队入队源码伍,该抢占资源抢占资源
public final void acquire(int arg) {
if (源码编辑器手机版下载!tryAcquire(arg) &&
acquireQ源码本钱ueued(addWaiter(Node.EXCLUSIVE源码编程), arg))
sel源码编辑器fInterrupt();
}