欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

Condition中的transferForSignal()方法的不解

发布时间:2024/8/26 编程问答 36 豆豆
生活随笔 收集整理的这篇文章主要介绍了 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

  • 在从condition队列到同步队列之前,tail节点被取消了

  • 在从condition队列到同步队列之后,tail节点没被取消,执行完ws > 0
    之后,这段时间,被取消了?就通过!compareAndSetWaitStatus(p, ws, Node.SIGNAL)再判断一下

    这个时候偷偷的提前执行一波,就算不提前执行,AQS队列会不断的把取消节点取消掉,最终还是被unpark的。
    总的意思就是为了提升性能,没有这里也是合理的。

  • 总结

    以上是生活随笔为你收集整理的Condition中的transferForSignal()方法的不解的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。