Condition的await-signal流程详解(转)
上一篇文章梳理了condtion,其中侧重流程,网上看到这篇文章文章介绍的很细。值得学习。特意转载过来。
转载请注明出处:http://blog.csdn.net/luonanqin
转载路径:http://blog.csdn.net/bohu83/article/details/51107870
上一篇讲了ReentrantLock的lock-unlock流程,今天这篇讲讲Condition的await-signal流程。
Condition类图:
- Condition接口包含了多种await方式和两个通知方法
- ConditionObject实现了Condition接口,是AbstractQueuedSynchronizer的内部类
- Reentrantlock的newCondition方法返回与某个lock实例相关的Condition对象
和release队列一样,Condition队列也是虚拟队列,每个Node通过nextWaiter进行关联。因为Condition Node要变为release Node才可以解除阻塞,所以不需要prevWaiter,这一点下面会有说明。
大概的整个过程是:
调用await的线程都会进入一个Condition队列。调用signal的线程每一次都会从firstWaiter开始找出未取消的Condition Node放到release队列里,然后调用signal的线程在await或者unlock的时候执行release方法才有机会将其解除阻塞。相对于lock-unlock,正常的流程要简单一些,但是对于中断处理会更为复杂。
先看看调用await()至阻塞的过程
如图所示,该过程可分为三个步骤:
- 新建Condition Node包装线程,加入Condition队列
- 释放当前线程占用的锁
- 阻塞当前线程
// AbstractQueuedSynchronizer.ConditionObject.class
final boolean isOnSyncQueue(Node node) { // 当进入Condition队列时,waitStatus肯定为CONDITION,如果同时别的线程调用signal,Node会从Condition队列中移除,并且移除时会清除CONDITION状态。 // 从移除到进入release队列,中间这段时间prev必然为null,所以还是返回false,即被park if (node.waitStatus == Node.CONDITION || node.prev == null) return false; // 当别的线程进入release队列时,会和前一个Node建立前后关系,所以如果next存在,说明一定在release队列中 if (node.next != null) // If has successor, it must be on queue return true; /* * node.prev can be non-null, but not yet on queue because * the CAS to place it on queue can fail. So we have to * traverse from tail to make sure it actually made it. It * will always be near the tail in calls to this method, and * unless the CAS failed (which is unlikely), it will be * there, so we hardly ever traverse much. */ // 可能该Node刚刚最后一个进入release队列,所以是tail,其next必然是null,所以需要从队尾向前查找 return findNodeFromTail(node); }
signal()流程图
// AbstractQueuedSynchronizer.ConditionObject.class
final boolean transferForSignal(Node node) { /* * If cannot change waitStatus, the node has been cancelled. */ // 如果改变waitStatus失败,说明已经被取消,没必要再进入release队列了。外部再循环找到一个Condition Node // 如果改变waitStatus成功,但是之后又被取消会怎么样?没关系,虽然已经进入release队列了,但是release方法里的unpark操作会跳过已取消的Node。这里的检查只是为了减少unpark时不必要的工作 if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) return false; /* * Splice onto queue and try to set waitStatus of predecessor to * indicate that thread is (probably) waiting. If cancelled or * attempt to set waitStatus fails, wake up to resync (in which * case the waitStatus can be transiently and harmlessly wrong). */ // p是该Node的前驱 Node p = enq(node); int ws = p.waitStatus; // 这里影响设置waitStatus只可能发生于线程被取消,那时会调用cancelAcquire方法将waitStatus设置为CANCEL,但它不是CAS的 if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) LockSupport.unpark(node.thread); return true; }
我们可以看到,signal方法只是将Node修改了状态,并没有唤醒线程。要将修改状态后的Node唤醒,一种是再次调用await(),一种是调用unlock()。这两个方法内部都会执行release方法对release队列里的Node解除阻塞,关于这点我在上一篇文章里已经说明了。
之后有时间我会把一些中断处理也用流程图描述下发出来。
最新文章
- 。。。验证码,发送不同的GET请求。。。
- (22)odoo 安装旧模块报错处理
- ubuntu 下添加sublime text 启动图标
- Linux解压缩总结
- MAVEN 打包WAR
- vue中使用keepAlive组件缓存遇到的坑
- MySQL表介绍
- .net ElasticSearch-Sql 扩展类【原创】
- html页面技巧
- 干货 unity小贴士
- 解决php的交互式命令行不能正常启动的问题兼介绍psysh
- AngularJS 启程二
- 如何基于TensorFlow使用LSTM和CNN实现时序分类任务
- Linux 删除文件夹和文件的命令(转载)
- Mongodb C#客户端数据关联数据,使用Linq语法进行关联
- 2-4 js基础-事件对象小结
- C# 调用C++DLL 类型转换
- js无缝滚动,不平滑(求高人指点)
- Android4.0 Surface机制分析
- 基于nc实现聊天