2021-05-04 19:36  阅读(178)
文章分类:从 JDK 源码看 Java 文章标签:JavaJDK 源码
©  原文作者:超人汪小建(seaboat) 原文地址:https://blog.csdn.net/wangyangzhizhou/column/info/16032

目前在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中提供的方法操作。

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> 从JDK源码角度看线程的阻塞和唤醒
上一篇
从JDK源码看String(下)
下一篇
从JDK源码角度看并发锁的优化