CLH同步队列

article/2025/11/5 19:49:05

文章转载自:https://blog.csdn.net/chenssy/article/details/60781148

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


 


http://chatgpt.dhexx.cn/article/UYhFeYyf.shtml

相关文章

AQS的前菜—详解CLH队列锁

https://blog.csdn.net/weixin_47184173/article/details/115340014 什么是CLH队列锁 CLH锁其实就是一种基于逻辑队列非线程饥饿的一种自旋公平锁。当多线程竞争一把锁时,获取不到锁的线程,会排队进入CLH队列的队尾,然后自旋等待&#xff0c…

CLH(Craig, Landin, and Hagersten locks)机制

CLH(Craig, Landin, and Hagersten locks): 是一个自旋锁,能确保无饥饿性,提供先来先服务的公平性。CLH锁也是一种基于链表的可扩展、高性能、公平的自旋锁,申请线程只在本地变量上自旋,它不断轮询前驱的状态,如果发现…

【Java】Java函数式编程以及流的操作

文章目录 大纲lambda表达式一般内部类局部内部类匿名内部类 基于函数式接口的lambda表达式JDK8中自带的函数式接口Predicate判断Consumer消费Supplier供应商Function方法其他接口 方法引用和构造器引用静态方法引用非静态方法引用构造器引用 lambda表达式中的异常处理Currying …

【Java进阶】java函数式编程的使用

目录 1.目前Java中自带的函数式编程接口 2.java中使用函数式编程的案例 3.自定义函数式接口 4.自定义函数式接口的实现 简单一句话理解函数式编程,传统的方法调用我们都是传递参数,而函数式编程,传递的则是方法实现的过程。 1.目前Java中自…

Java教程:一文详解函数式编程

看懂了这篇-你就懂了函数式接口 ​ 函数式编程是一种编程规范或一种编程思想,简单可以理解问将运算或实现过程看做是函数的计算。 Java8为了实现函数式编程,提出了3个重要的概念:Lambda表达式、方法引用、函数式接口。现在很多公司都在使用l…

java中的函数式编程(一)

当你安安稳稳的学着java,慢慢开始写代码。 兢兢业业,本着面向对象的编程方式。 知道有一种叫做“面向过程”的方式,但是你不在意。 写了一段时间后有人告你,函数式编程很爽! 你也看到了他给的一个实例,看着…

Java函数式编程(基础):第一部分

1.函数式编程有三个部分: 第一个部分是:Lambda表达式 第二个部分是:方法引用 第三个部分是:函数式接口 刚接触Lambda表达式的我,觉得它很神奇,能够用简短的代码,代替传统的编程方式 举一个简…

java-函数式编程浅谈

了解函数式编程的实际应用场景以及优点。 文章目录 什么是函数式编程函数式编程的使用原理解析 什么是函数式编程 以数学中的函数作为切入点,只关注参数之间的运算满足某种规则,例如zxy。 那么如何体现在编程中呢,熟知的function定义可以作…

Java 8函数式编程

函数式接口 一个接口中,有且只有一个抽象方法,这个接口就叫做函数式接口。常常使用FunctionalInterface注解作为编译校验。满足函数式接口的要求,才能校验通过,否则会在校验阶段失败。 接口中有且只能有一个抽象方法,…

【函数式编程实战】(一)Java演变与函数式编程

前言 📫作者简介:小明Java问道之路,专注于研究计算机底层/Java/Liunx 内核,就职于大型金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的架构设计📫 🏆CSDN专家博主/Java领域优质…

Java8 函数式编程

文章目录 Java 函数式编程1. Lambda 表达式1.1 标准格式1.2 使用前提1.2.1 一个参数1.2.2 多个参数1.2.3 有返回值 1.3 省略简化1.4 函数式接口1.4.1 Supplier1.4.2 Consumer1.4.3 Predicate1.4.4 Function 1.5 方法引用1.5.1 对象 :: 实例方法1.5.2 类 :: 静态方法1.5.3 类 ::…

入门 Java 函数式编程,看完这篇就清晰了

Java 在最开始是不支持函数式编程的,想来也好理解,因为在 Java 中类 Class 才是第一等公民,这就导致在 Java 中实现编程不是件那么容易的事儿,不过虽然难,但是结果我们也已经知道了,在 Java 8 这个大版本里…

Java函数式编程详解

Java从1.8以后引入了函数式编程,这是很大的一个改进。函数式编程的优点在提高编码的效率,增强代码的可读性。本文历时两个多月一点点写出来,即作为心得,亦作为交流。 1.Java函数式编程的语法: 使用Consumer作为示例&…

Java 函数式编程 详细介绍

在兼顾面向对象特性的基础上,Java语言通过Lambda表达式与方法引用等,为开发者打开了函数式编程的大门。 下面我们做一个初探。 Lambda的延迟执行 有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。而Lambda表达式是延…

Java基础函数式编程

本篇博文目录: 前言1.什么是函数式接口2.函数式编程(1) 使用Lambda表达式(2) Lambda表达式的进一步简化(3) Java内置函数式接口 3.方法引用(1) 方法引用的简单使用(2) 方法引用的分类 4.Stream API(1) 什么是Stream(2) 流式操作的执行流程(3) Stream的创建(4) Stream中间操作(5…

Java8新特性【函数式编程API、新时间日期处理API、Optional容器类】总结

文章目录 1、Lambda表达式1.1什么是Lambda表达式1.2从匿名类到 Lambda 的转换1.3Lambda表达式语法 2、函数式接口2.1什么是函数式接口2.2自定义函数式接口2.3内置核心函数式接口2.4接口中常用的默认方法 3、方法引用与构造器引用3.1 推荐用法3.2 基本格式3.3 语法详解(了解)3.3…

一文带你入门 Java 函数式编程

Java 在最开始是不支持函数式编程的,想来也好理解,因为在 Java 中类 Class 才是第一等公民,这就导致在 Java 中实现编程不是件那么容易的事儿,不过虽然难,但是结果我们也已经知道了,在 Java 8 这个大版本里…

Oracle数据库 存储过程入门

oracle存储过程:简单入门 一、定义 存储过程是一组为了完成特定功能的SQL语句,经编译后存储在数据库中。点击查看优缺点。二、存储过程简单入门 ***第一个存储过程:打印hello word, my name is stored procedure内容*** create or replace procedure m…

数据库储存过程超简单实例

网上看了半天都没找到一个完整储存过程从创建到调用的实例,于是自己写了一个简单的实例. 数据库创建存储过程,定义个函数 格式如下,开头DELIMITER //和结尾/DELIMITER 和BEGIN 和 END 是固定格式 定了一个叫test2()的方法(在mapper.xml中会指定这个函数名),in表示入参,varc…

DM8达梦数据库存储过程函数使用

DM8数据库的过程函数的编写主要分为4个部分:过程头部分,声明定义部分,执行部分和异常处理部分。在编写方面,过程和函数的主要区别还是函数可以返回一个值,但是过程没有。下面就从这4个部分来分别介绍过程的编写和一些常…