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

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

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

static final class Node {

/** 共享 */

static final Node SHARED = new Node();

/** 独占 */

static final Node EXCLUSIVE = null;

/**

* 因为超时或者中断,节点会被设置为取消状态,被取消的节点时不会参与到竞争中的,他会一直保持取消状态不会转变为其他状态;

*/

static final int CANCELLED = 1;

/**

* 后继节点的线程处于等待状态,而当前节点的线程如果释放了同步状态或者被取消,将会通知后继节点,使后继节点的线程得以运行

*/

static final int SIGNAL = -1;

/**

* 节点在等待队列中,节点线程等待在Condition上,当其他线程对Condition调用了signal()后,改节点将会从等待队列中转移到同步队列中,加入到同步状态的获取中

*/

static final int CONDITION = -2;

/**

* 表示下一次共享式同步状态获取将会无条件地传播下去

*/

static final int PROPAGATE = -3;

/** 等待状态 */

volatile int waitStatus;

/** 前驱节点 */

volatile Node prev;

/** 后继节点 */

volatile Node next;

/** 获取同步状态的线程 */

volatile Thread thread;

Node nextWaiter;

final boolean isShared() {

return nextWaiter == SHARED;

}

final Node predecessor() throws NullPointerException {

Node p = prev;

if (p == null)

throw new NullPointerException();

else

return p;

}

Node() {

}

Node(Thread thread, Node mode) {

this.nextWaiter = mode;

this.thread = thread;

}

Node(Thread thread, int waitStatus) {

this.waitStatus = waitStatus;

this.thread = thread;

}

}

CLH同步队列结构图如下:

入列

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

private Node addWaiter(Node mode) {

//新建Node

Node node = new Node(Thread.currentThread(), mode);

//快速尝试添加尾节点

Node pred = tail;

if (pred != null) {

node.prev = pred;

//CAS设置尾节点

if (compareAndSetTail(pred, node)) {

pred.next = node;

return node;

}

}

//多次尝试

enq(node);

return node;

}

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

private Node enq(final Node node) {

//多次尝试,直到成功为止

for (;;) {

Node t = tail;

//tail不存在,设置为首节点

if (t == null) {

if (compareAndSetHead(new Node()))

tail = head;

} else {

//设置为尾节点

node.prev = t;

if (compareAndSetTail(t, node)) {

t.next = node;

return t;

}

}

}

}

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

过程图如下:

出列

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

参考资料

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

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

java clh_【死磕Java并发】-J.U.C之AQS:CLH同步队列 - Java 技术驿站-Java 技术驿站相关推荐

  1. 死磕Java并发:J.U.C之AQS:CLH同步队列

    本文转载自公号:Java技术驿站 在上篇文章"死磕Java并发:J.U.C之AQS简介"中提到了AQS内部维护着一个FIFO队列,该队列就是CLH同步队列. CLH同步队列是一个F ...

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

    原文出处:https://www.cmsblogs.com/category/1391296887813967872 『chenssy』 在上篇博客[死磕Java并发]-----J.U.C之AQS:A ...

  3. java condition_死磕Java并发:J.U.C之Condition

    在没有Lock之前,我们使用synchronized来控制同步,配合Object的wait().notify()系列方法可以实现等待/通知模式.在Java SE5后,Java提供了Lock接口,相对于 ...

  4. Java并发J.U.C 之 AQS

    J.U.C 之 AQS AbStractQueuedSynchronizer类,简称AQS,是一个来构建锁和同步器的框架,JDK1.5开始引入了J.U.C,大大提高了JAVA程序的并发性,而AQS则是 ...

  5. java arraydeque_死磕 java集合之ArrayDeque源码分析

    问题 (1)什么是双端队列? (2)ArrayDeque是怎么实现双端队列的? (3)ArrayDeque是线程安全的吗? (4)ArrayDeque是有界的吗? 简介 双端队列是一种特殊的队列,它的 ...

  6. java condition_死磕 java同步系列之ReentrantLock源码解析(二)

    (手机横屏看源码更方便) 问题 (1)条件锁是什么? (2)条件锁适用于什么场景? (3)条件锁的await()是在其它线程signal()的时候唤醒的吗? 简介 条件锁,是指在获取锁之后发现当前业务 ...

  7. aqs clh java_【Java并发编程实战】—– AQS(四):CLH同步队列

    在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形. 其主要从双方面进行了改造:节点的结构与节点等待机制.在结构上引入了 ...

  8. java arraylist_死磕 java集合之ArrayList源码分析

    简介 ArrayList是一种以数组实现的List,与数组相比,它具有动态扩展的能力,因此也可称之为动态数组. 继承体系 ArrayList实现了List, RandomAccess, Cloneab ...

  9. java队列加锁_java并发-----浅析ReentrantLock加锁,解锁过程,公平锁非公平锁,AQS入门,CLH同步队列...

    前言 为什么需要去了解AQS,AQS,AbstractQueuedSynchronizer,即队列同步器.它是构建锁或者其他同步组件的基础框架(如ReentrantLock.ReentrantRead ...

  10. 死磕Java并发:J.U.C之阻塞队列:ArrayBlockingQueue

    作者:chenssy 来源:Java技术驿站 ArrayBlockingQueue,一个由数组实现的有界阻塞队列.该队列采用FIFO的原则对元素进行排序添加的. ArrayBlockingQueue为 ...

最新文章

  1. ruby中取反的问题
  2. java 跨域_springboot解决跨域CROS问题,用注解@CrossOrigin
  3. Win7下面wubi安装Ubuntu14.04LTS
  4. kafka基本管理操作命令
  5. 运算放大器基本公式_运算放大器 - 产生的背景和解决的问题
  6. jzoj3518-进化序列(evolve)【位运算】
  7. php保存gbk字符串,php判断字符串gbk/utf8编码和转换
  8. Qt_IOS环境搭建 Qt for ios Projector ERROR:This mkspec requires Xcode 4.3 or later
  9. Matlab资料汇总暨MATLAB中文论坛帖子整理(二)
  10. 路飞学城Python-Day1
  11. gnu nano显卡测试软件,显卡天梯图2018年9月最新版 秒懂桌面显卡性能排行
  12. Windows7安装VM12时出现:安装程序无法继续 microsoft runtime dll
  13. 码蹄集第23周赛(买礼物,召唤神龙,大促销,轨道探测)
  14. 万豪酒店集团5亿客户记录泄露
  15. Android 听筒 扬声器 切换
  16. 排队叫号医院管理源码
  17. 阿里巴巴普惠字体下载链接
  18. CSS-动效 纯css粒子动效 | 转圈圈 | 上下弹动
  19. 截图工具(win+shift+S)截图之后为什么粘贴不上?有了这个方法,不用单独下载多次复制粘贴的工具了
  20. 宝塔装两个mysql_同时安装Appnode与宝塔,宝塔创建Mysql数据库不同步问题

热门文章

  1. 九寨沟地震类毕业论文文献都有哪些?
  2. C#毕业设计——基于C#+asp.net+sqlserver的汽车修理厂物资流通管理系统设计与实现(毕业论文+程序源码)——物资流通管理系统
  3. Python中一些少数人知晓且有趣的特性
  4. python 有趣包_一些有趣且鲜为人知的 Python 特性
  5. (C语言)不变初心数 (15 分)---pta
  6. PHP输出分割线,dede标签调用大全dedecms隔五行一个分割线_PHP教程
  7. element-ui el-dialog 的form 表单验证关闭时清除错误验证信息
  8. python根据时间序列画折线图_时间序列模型的python实现
  9. 如何用 Telemetry 测试移动 APP H5性能?
  10. android摇一摇切换配置,逍遥模拟器也可以摇一摇了 附设置教程