CLH队列

在AQS类文件的开头,作者添加了很长一段注释,向开发者解释CLH队列,以及AQS对CLH队列的使用。AQS里面的CLH队列是CLH同步锁的一种变形。其主要从两方面进行了改造:节点的结构与节点等待机制。在结构上,AQS类引入了头结点和尾节点,他们分别指向队列的头和尾,尝试获取锁、入队列、释放锁等实现都与头尾节点相关:

To enqueue into a CLH lock, you atomically splice it in as new tail. To dequeue, you just set the head field.即要加入CLH锁,可以自动将其作为新尾部进行拼接。 要出队,您只需设置头字段。

Node

CLH队列由Node对象组成,Node是AQS中的内部类。

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唤醒*/static final int CONDITION = -2;/*** 表示下一次共享式同步状态获取将会无条件地传播下去*/static final int PROPAGATE = -3;/*** 等待状态*/volatile int waitStatus;/*** 前驱节点*/volatile Node prev;/*** 后继节点*/volatile Node next;/*** 节点线程*/volatile Thread thread;Node nextWaiter;/*** Returns true if node is waiting in shared mode.*/final boolean isShared() {return nextWaiter == SHARED;}Node() {    // Used to establish initial head or SHARED marker}Node(Thread thread, Node mode) {     // Used by addWaiterthis.nextWaiter = mode;this.thread = thread;}Node(Thread thread, int waitStatus) { // Used by Conditionthis.waitStatus = waitStatus;this.thread = thread;}
......}

CLH队列执行

1.线程调用acquire方法获取锁,如果获取失败则会进入CLH队列

public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}

2.addWaiter(Node.EXCLUSIVE)方法会将当前线程封装成Node节点,追加在队尾。

private Node addWaiter(Node mode) {Node node = new Node(Thread.currentThread(), mode);// 获取原队尾Node pred = tail;if (pred != null) {node.prev = pred;//用cas更新 ,pred是原来队尾,作为预期值,node作为新值if (compareAndSetTail(pred, node)) {pred.next = node;return node;}}//前面cas更新失败后,再enq方法中循环用cas更新直到成功enq(node);return node;}

enq方法:

private Node enq(final Node node) {for (;;) {Node t = tail;if (t == null) { //使用cas初始化head节点if (compareAndSetHead(new Node()))tail = head;} else {//将当前节点放在clh队列尾部node.prev = t;if (compareAndSetTail(t, node)) {t.next = node;return t;}}}}

假如有两个线程,第一个执行中,第二个在lch队列里面,在return node处,此时debug:

接下来是acquireQueued方法,他会使线程自旋阻塞,直到获取到锁。

final boolean acquireQueued(final Node node, int arg) {boolean failed = true;try {boolean interrupted = false;for (;;) {//1. 拿到当前节点的前置节点final Node p = node.predecessor();//2. 如果当前节点的前置节点是头节点的话,就再次尝试获取锁if (p == head && tryAcquire(arg)) {//成功获取锁后,将节点设置为头节点setHead(node);p.next = null; // help GCfailed = false;return interrupted;}/**更改当前节点前置节点的waitStatus,只有前置节点的waitStatus=Node.SIGNAL,当前节点才有可能被唤醒。如果前置节点的waitStatus>0(即取消),则跳过取更前面的节点。*/if (shouldParkAfterFailedAcquire(p, node) &&//通过Unsafe.park来阻塞线程parkAndCheckInterrupt())interrupted = true;}} finally {if (failed)cancelAcquire(node);}}
 private final boolean parkAndCheckInterrupt() {//在此阻塞,收到unlock()方法的unPark()方法会被唤醒LockSupport.park(this);return Thread.interrupted();}

线程释放锁,从前面可以知道,获取到锁的线程会设置为CLH队列的头部。这里如果tryRelease返回true,且head的waitStatus!=0。就会更新head的waitStatus为0并且 唤醒线程head.next节点的线程。

 public final boolean release(int arg) { //判断是否可以释放锁。if (tryRelease(arg)) {Node h = head;if (h != null && h.waitStatus != 0)unparkSuccessor(h);return true;}return false;}
 private void unparkSuccessor(Node node) {int ws = node.waitStatus;//waitStatus不是取消状态,就设置成0if (ws < 0)compareAndSetWaitStatus(node, ws, 0);//获取下个waitStatus不为取消的NodeNode s = node.next;if (s == null || s.waitStatus > 0) {s = null;for (Node t = tail; t != null && t != node; t = t.prev)if (t.waitStatus <= 0)s = t;}//LockSupport.unpark是调用了Unsafe.unpark,唤醒线程。if (s != null)LockSupport.unpark(s.thread);}

总结:

其实aqs的核心原理无非三点:1.自旋 2.Lock.park()和unpark() 3.cas,顺着这三个关键点和加锁以及解锁过程就可以理清

参考文章:

https://blog.csdn.net/qq_26680031/article/details/82348053

https://blog.csdn.net/java_lyvee/article/details/98966684

并发编程之AQS中的CLH队列相关推荐

  1. 高并发编程_高并发编程系列:全面剖析Java并发编程之AQS的核心实现

    在并发编程领域,AQS号称是并发同步组件的基石,很多并发同步组件都是基于AQS实现,所以想掌握好高并发编程,你需要掌握好AQS. 本篇主要通过对AQS的实现原理.数据模型.资源共享方式.获取锁的过程, ...

  2. Java并发编程之AQS详解

    一.概述 谈到并发,不得不谈ReentrantLock:而谈到ReentrantLock,不得不谈AbstractQueuedSynchronizer(AQS)! 类如其名,抽象的队列式的同步器,AQ ...

  3. Java并发编程之AQS以及源码解析

    文章目录 概览 实现思路 实现原理 源自CLH锁 AQS数据模型 CAS操作 主要方法 自定义同步器的实现方法 AQS定义的模板方法 源码解读 等待状态释义 AQS获取锁的流程图 获取独占锁的实现 总 ...

  4. java 并发编程 aqs_Java并发编程之AQS

    一.什么是AQS AQS(AbstractQueuedSynchronize:队列同步器)是用来构建锁或者其他同步组件的基础框架,很多同步类都是在它的基础上实现的,比如常用的ReentrantLock ...

  5. zbb20180929 thread java并发编程之Condition

    java并发编程之Condition 引言 在java中,对于任意一个java对象,它都拥有一组定义在java.lang.Object上监视器方法,包括wait(),wait(long timeout ...

  6. 并发编程之LockSupport的 park 方法及线程中断响应

    系列文章目录 Java并发编程技术知识点梳理(第一篇)操作系统底层工作的整体认识 Java并发编程技术知识点梳理(第二篇)并发编程之JMM&volatile详解 Java并发编程技术知识点梳理 ...

  7. 并发编程之 Executor 线程池原理与源码解读

    并发编程之 Executor 线程池原理与源码解读 线程是调度 CPU 资源的最小单位,线程模型分为 KLT 模型与 ULT 模型,JVM使用的是 KLT 模型.java线程与 OS 线程保持 1:1 ...

  8. Python并发编程之threading模块

    Python并发编程之threading模块 threading 模块 1. Timer对象 2. Lock对象 3. RLock 4. 信号量和有边界的信号量 5. 事件 6. 条件变量 7. 使用 ...

  9. Java并发编程之CyclicBarrier详解

    简介 栅栏类似于闭锁,它能阻塞一组线程直到某个事件的发生.栅栏与闭锁的关键区别在于,所有的线程必须同时到达栅栏位置,才能继续执行.闭锁用于等待事件,而栅栏用于等待其他线程. CyclicBarrier ...

  10. cyclicbarrier java_Java并发编程之CyclicBarrier和线程池的使用

    原标题:Java并发编程之CyclicBarrier和线程池的使用 下面我们来讲述一下线程池和CyclicBarrier的使用和对比. 一.场景描述 有四个游戏玩爱好者玩游戏,游戏中有三个关卡,每一个 ...

最新文章

  1. 【转载】学习嵌入式系统需要具备的条件、方法及步骤
  2. (0029) iOS 开发之API HTTP 请求调试网站
  3. tengine 调用php,nginx 或tengine 访问日志分割处理
  4. java常见的内存泄漏
  5. music算法_Elasticsearch系列---相关性评分算法及正排索引
  6. Cocos Creator中的动画支持技术
  7. 殷浩详解DDD:领域层设计规范
  8. 基于matlab的prony方法实现,基于MATLAB的Prony方法实现
  9. 20172319 《程序设计与数据结构》实验一报告
  10. 深入理解Flex布局以及计算
  11. 对象tostring后怎么转成对象_和女生相亲后怎么联系对方?和相亲对象该如何聊天...
  12. yii2 mysql 队列_yii2.0 中的队列
  13. Matlab遗传算法TSP求解
  14. xsmax是大黑边?_苹果iPhone11和xsmax,8p x xr xs怎么选?干货分享!
  15. 电路图中电阻分类字母速记说明图文
  16. 解决Elasticsearch集群 master_not_discovered_exception 异常
  17. 应用计算机怎么弹ink,ink是什么文件,手把手教你怎么打开ink文件
  18. eclipse鼠标变成十字架
  19. win7修改计算机名访问被拒绝访问,今天解答win7无法更改注册表拒绝访问的解决介绍...
  20. 简单内存泄漏检测方法,解决Detected memory leaks!问题 .

热门文章

  1. 暴雪战网服务器维护,炉石无法通过暴雪战网服务进行登录
  2. element-ui upload 多个文件一次请求上传(Vue精简版)
  3. D. Concatenated Multiples
  4. margin-top传递问题
  5. 云计算简介及Kubernetes云平台搭建
  6. Illegal base64 character 20
  7. check_cbss_kafka.sh
  8. 给每一辆车配上“×××”,老牌安企高新兴的交通新作
  9. 〖2011.08.19〗秋无痕常用软件全功能装机光盘2011年八月版(支持64位WIN7)
  10. Git介绍、安装、环境配置及常用的Git命令