从 JDK 源码角度看线程的阻塞和唤醒

目前在Java语言层面能实现阻塞唤醒的方式一共有三种:suspend与resume组合、wait与notify组合、park与unpark组合。javascript

其中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

Java真正意义上的语言层面上的并发编程应该从并发专家Doug Lea领导的JSR-166开始,此规范请求向JCP提交了向Java语言中添加并发编程工具,即在jdk中添加java.util.concurrent工具包供开发者使用,开发者能够轻松构建本身的同步器,而在此以前并发过程当中同步都只能依靠JVM内置的管程。node

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中提供的方法操做。post

相关阅读:
从JDK源码角度看并发竞争的超时
从 JDK 源码角度看 Java 并发的公平性spa

欢迎关注:
线程

相关文章
相关标签/搜索