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来保证的,因为只有一个线程能够成功获取到同步状态。过程图如下:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家

java clh队列什么意思_浅谈Java并发 J.U.C之AQS:CLH同步队列相关推荐

  1. java方法区对象类型_浅谈Java内存区域与对象创建过程

    一.java内存区域 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有的区域则 ...

  2. java中单例的应用_浅谈Java中单例模式的几种应用

    目录 浅谈Java中单例模式的几种应用 第一种:懒汉式 第二种:饿汉式 第三种:双重检索式 第四种:注册登记式 第五种:内部类形式 浅谈Java中单例模式的几种应用 日常开发中,为了提高我们系统中对象 ...

  3. java的向下转型_浅谈Java向下转型的意义

    一开始学习 Java 时不重视向下转型.一直搞不清楚向下转型的意义和用途,不清楚其实就是不会,那开发的过程肯定也想不到用向下转型. 其实向上转型和向下转型都是很重要的,可能我们平时见向上转型多一点,向 ...

  4. php和java的区别菜鸟教程_浅谈Java和PHP的主要区别

    当谈到PHP与Java的差异性问题时,更多的是回答初学者的一些疑问.对于刚接触IT的同学来说,他们需要做好对未来职业的选择.所以是选择PHP还是选择Java更有利于自身的技术特点和发展前景.所以在解决 ...

  5. java字符串常量存哪里_浅谈JAVA中字符串常量的储存位置

    在讲述这些之前我们需要一些预备知识: Java的内存结构我们可以通过两个方面去看待它. 从该角度看的话Java内存结构包含以下部分:该部分内容可以结合:JVM简介(更加详细深入的介绍) 1.栈区:由编 ...

  6. java类的命名规范_浅谈Java中的命名规范

    现代软件架构的复杂性需要协同开发完成,如何高效地协同呢? 答案是:制定一整套统一的规范. 无规矩不成方圆,无规范难以协同,比如,制订交通法规表面上是要限制行车权,实际上是保障公众的人身安全,试想如果没 ...

  7. java接口是干啥_浅谈Java接口

    接口(英文:Interface)是Java中非常重要的内容,初学的时候可能感受不深,但是在做项目的时候,对面向接口编程的运用就变得尤为重要,不过这是后话了.现在先讨论假如是刚刚接触接口这个概念,该怎么 ...

  8. java四种内部类区别_浅谈Java中的四种内部类

    如果你看过一些JDK和框架源码的话,就经常会发现一般在类的定义中,都会再定义一些其他的类,这些类也同样会被编译成字节码文件,这样的类就被叫做 内部类 ,按照一般的分法,大致可以分为以下四类: 成员内部 ...

  9. java 多线程同步_浅谈Java多线程(状态、同步等)

    Java多线程是Java程序员必须掌握的基本的知识点,这块知识点比较复杂,知识点也比较多,今天我们一一来聊下Java多线程,系统的整理下这部分内容. 一.Java中线程创建的三种方式: 1.通过继承T ...

  10. java 中的单元测试_浅谈Java 中的单元测试

    单元测试编写 Junit 单元测试框架 对于Java语言而言,其单元测试框架,有Junit和TestNG这两种, 下面是一个典型的JUnit测试类的结构 package com.example.dem ...

最新文章

  1. 宅男程序员给老婆的计算机课程
  2. HDU 4121 Xiangqi
  3. 彻底学会Spring事务——概念+具体实现
  4. 特斯拉CEO马斯克再次动员员工 加快季度末交付
  5. python3两个三阶矩阵相乘公式_Numpy 两个矩阵部分维度相乘,有没有很快的方法?...
  6. spring-data-redis和jedis版本对应问题
  7. 什么样的状态该跳槽了?
  8. java语言程序设计第三版电子书百度云_Java语言程序设计(基础篇)(原书第10版) 完整版 中文pdf扫描版[259MB]梁勇...
  9. God‘s Perspective - God View - 上帝视角
  10. 赵丽颖冯绍峰结婚官宣!深扒2人10亿商业关系:女方年入过亿,男方是乐视股东...
  11. 分布积分详解(lnx积分)
  12. git 打patch的方法
  13. exp/expdp 与 imp/impdp命令导入导出数据库详解
  14. MySQL数据库表的插入,修改,删除操作实验
  15. linux忘记root密码怎么办——重置root密码的四种方法
  16. 如果我说熟悉SpringBoot 面试官会怎么问?
  17. JAVA基础-栈与堆,static、final修饰符、内部类和Java内存分配
  18. 基于STM32的DHT11温湿度传感器设计
  19. java阿里云短信服务SDK使用方法
  20. squid服务的应用[转]

热门文章

  1. uniapp实现退款理由的界面
  2. TesterHome android app 编写历程(五)
  3. MySQL 英文格式日期转换
  4. 后端开发的定义及意义PHP,【后端开发】php接口有什么作用
  5. DSPE-PEG3-Mal,小分子PEG连接马来酰亚胺基团
  6. RxSwift学习(一) --- RxSwift介绍
  7. 20165219王彦博课上考试总结
  8. 威联通nas怎么更换大硬盘_QNAP 篇一:记一次换硬盘引发的折腾
  9. python:输出10行的杨辉三角 - 二项式的n次方展开系数
  10. 百度地图获取河流_开车旅行少不了地图导航,高德地图和百度地图,你觉得哪个好用?...