欢迎光临
我们一直在努力

【死磕Java并发】—– J.U.C之AQS:CLH同步队列

原文出处:http://cmsblogs.com/ 『chenssy』

在上篇博客【死磕Java并发】—–J.U.C之AQS:AQS简介中提到了AQS内部维护着一个FIFO队列,该队列就是CLH同步队列。

CLH同步队列是一个FIFO双向队列,AQS依赖它来完成同步状态的管理,当前线程如果获取同步状态失败时,AQS则会将当前线程已经等待状态等信息构造成一个节点(Node)并将其加入到CLH同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点唤醒(公平锁),使其再次尝试获取同步状态。

在CLH同步队列中,一个节点表示一个线程,它保存着线程的引用(thread)、状态(waitStatus)、前驱节点(prev)、后继节点(next),其定义如下:

【死磕Java并发】—– J.U.C之AQS:CLH同步队列

【死磕Java并发】—– J.U.C之AQS:CLH同步队列

CLH同步队列结构图如下:

【死磕Java并发】—– J.U.C之AQS:CLH同步队列

入列

学了数据结构的我们,CLH队列入列是再简单不过了,无非就是tail指向新节点、新节点的prev指向当前最后的节点,当前最后一个节点的next指向当前节点。代码我们可以看看addWaiter(Node node)方法:

【死磕Java并发】—– J.U.C之AQS:CLH同步队列

addWaiter(Node node)先通过快速尝试设置尾节点,如果失败,则调用enq(Node node)方法设置尾节点

【死磕Java并发】—– J.U.C之AQS:CLH同步队列

在上面代码中,两个方法都是通过一个CAS方法compareAndSetTail(Node expect, Node update)来设置尾节点,该方法可以确保节点是线程安全添加的。在enq(Node node)方法中,AQS通过“死循环”的方式来保证节点可以正确添加,只有成功添加后,当前线程才会从该方法返回,否则会一直执行下去。

过程图如下:

【死磕Java并发】—– J.U.C之AQS:CLH同步队列

出列

CLH同步队列遵循FIFO,首节点的线程释放同步状态后,将会唤醒它的后继节点(next),而后继节点将会在获取同步状态成功时将自己设置为首节点,这个过程非常简单,head执行该节点并断开原首节点的next和当前节点的prev即可,注意在这个过程是不需要使用CAS来保证的,因为只有一个线程能够成功获取到同步状态。过程图如下:

【死磕Java并发】—– J.U.C之AQS:CLH同步队列

参考资料

Doug Lea:《Java并发编程实战》

方腾飞:《Java并发编程的艺术》

扩展阅读

【死磕Java并发】—– J.U.C之AQS:同步状态的获取与释放

【死磕Java并发】—–J.U.C之AQS:阻塞和唤醒线程

【死磕Java并发】—– J.U.C之重入锁:ReentrantLock

【死磕Java并发】—– J.U.C之读写锁:ReentrantReadWriteLock

【死磕Java并发】—– J.U.C之Condition

【死磕Java并发】—– 深入分析CAS

【死磕Java并发】—- J.U.C之并发工具类:CyclicBarrier

「死磕 Java 并发」J.U.C 之 Java并发容器:ConcurrentHashMap

 收藏 (0) 打赏

您可以选择一种方式赞助本站

支付宝扫一扫赞助

微信钱包扫描赞助

未经允许不得转载:英协网 » 【死磕Java并发】—– J.U.C之AQS:CLH同步队列

分享到: 生成海报
avatar

热门文章

  • 评论 抢沙发

    • QQ号
    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址

    登录

    忘记密码 ?

    切换登录

    注册

    我们将发送一封验证邮件至你的邮箱, 请正确填写以完成账号注册和激活