Condition中的transferForSignal()方法的不解

Node p = enq(node);
···
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);// 唤醒节点上的线程
        return true;
···

通过enq放入同步队列了,他自己会慢慢的争抢锁,就像synchronize中的wait,为什么这里要进行一次unpark?

先看下判断条件。
ws>0,说明是1,是CANCELLED状态,!compareAndSetWaitStatus(p, ws, Node.SIGNAL)) 什么时候把前一个节点(enq(node);返回当前节点的前一个节点,也就是原来的tail)赋值为SIGNAL状态失败?就是前一个节点是CANCELLED的时候,好吧这两个好像是一个命题。

接着看为什么这里要进行一次unpakr?

10-12行的迷惑性就来源于此。实际上,就算去掉10-12行也是满足正确性要求的。因为线程T2释放锁后,依然会将从队头开始的第一个非取消节点唤醒,该节点会继续ConditionObject#await()中的工作(稍后回去分析)。10-12行是为了进一步提升性能,针对两种情况:
如果插入node前,AQS内部等待队列的队尾节点就已经被取消,则满足wc > 0
如果插入node后,AQS内部等待队列的队尾节点已经稳定,满足tail.waitStatus == 0,但在执行ws > 0之后!compareAndSetWaitStatus(p, ws, Node.SIGNAL)之前被取消,则CAS也会失败,满足compareAndSetWaitStatus(p, ws, Node.SIGNAL) == false

这两种情况下,提前唤醒node能够在等待锁的同时,预先完成一部分ConditionObject#await()中无需同步的工作。这部分成本不能被轻易忽视,因为条件队列被应用最多的场景是高并发,大量线程累加起来的成本是很可观的。
链接:https://www.jianshu.com/p/a932c184db52

  1. 在从condition队列到同步队列之前,tail节点被取消了
  2. 在从condition队列到同步队列之后,tail节点没被取消,执行完ws > 0 之后,这段时间,被取消了?就通过!compareAndSetWaitStatus(p, ws, Node.SIGNAL)再判断一下
    这个时候偷偷的提前执行一波,就算不提前执行,AQS队列会不断的把取消节点取消掉,最终还是被unpark的。

总的意思就是为了提升性能,没有这里也是合理的。

发布了549 篇原创文章 · 获赞 3319 · 访问量 259万+

猜你喜欢

转载自blog.csdn.net/dataiyangu/article/details/105029337