目前在Java语言层面能实现阻塞唤醒的方式一共有三种:suspend与resume组合、wait与notify组合、park与unpark组合。其中suspend与resume因为存在无法解决的竟态问题而被Java废弃,同样,wait与notify也存在竟态条件,wait必须在notify之前执行,假如一个线程先执行notify再执行wait将可能导致一个线程永远阻塞,如此一来,必须要提出另外一种解决方案,就是park与unpark组合,它位于JDK的juc包下,应该也是因为当时编写juc时发现java现有方式无法解决问题而引入的新阻塞唤醒方式,由于park与unpark使用的是许可机制,许可最大为1,所以unpark与park操作不会累加,而且unpark可以在park之前执行,如unpark先执行,后面park将不阻塞。
Java真正意义上的语言层面上的并发编程应该从并发专家Doug Lea领导的JSR-166开始,此规范请求向JCP提交了向Java语言中添加并发编程工具,即在jdk中添加java.util.concurrent工具包供开发者使用,开发者可以轻松构建自己的同步器,而在此之前并发过程中同步都只能依靠JVM内置的管程。
JDK的并发包中最重要的一个框架就是ASQ框架,它的阻塞和唤醒使用的是LockSupport类的park与unpark方法,分别调用的是Unsafe类的park与unpark本地方法。逻辑如下:
阻塞
if(尝试获取锁失败) {
创建node
使用CAS方式把node插入到队列尾部
while(true){
if(尝试获取锁成功 并且 node的前驱节点为头节点){
把当前节点设置为头节点
跳出循环
}else{
使用CAS方式修改node前驱节点的waitStatus标识为signal
if(修改成功){
LockSupport.park();
}
}
}
唤醒
if(尝试释放锁成功){LockSupport.unpark(下一节点包含的线程);}
假如一条线程参与锁竞争,首先先尝试获取锁,失败的话创建节点并插入队列尾部,然后再次尝试获取锁,如若成功则不做其他任务处理直接返回,否则设置节点状态为待运行状态,最后使用LockSupport的park阻塞当前线程。前驱节点运行完后将尝试唤醒后继节点,使用的即是LockSupport的unpark唤醒。
总的来说,JDK提供的并发工具,在阻塞与唤醒操作方面由于suspend与resume存在各种各样问题,必须使用LockSupport中提供的方法操作。
====广告时间,可直接跳过====
鄙人的新书《Tomcat内核设计剖析》已经在京东预售了,有需要的朋友可以到https://item.jd.com/12185360.html 进行预定。感谢各位朋友。
=========================
欢迎关注: