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

article/2025/11/5 19:47:13

CLH(Craig, Landin, and Hagersten locks):

  1. 是一个自旋锁,能确保无饥饿性,提供先来先服务的公平性。
  2. CLH锁也是一种基于链表的可扩展、高性能、公平的自旋锁,申请线程只在本地变量上自旋,它不断轮询前驱的状态,如果发现前驱释放了锁就结束自旋。
  3. AQS就是基于CLH队列,AQS是将每一条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node),来实现锁的分配。
    当一个线程需要获取锁时:
1 创建一个的QNode,将其中的locked设置为true表示需要获取锁       
2.线程对tail域调用getAndSet方法,使自己成为队列的尾部,同时获取一个指向其前趋结点的引用myPred                                
3.该线程就在前趋结点的locked字段上旋转,直到前趋结点释放锁
4.当一个线程需要释放锁时,将当前结点的locked域设置为false,同时回收前趋结点
如下图,线程A需要获取锁,其myNode域为true,tail指向线程A的结点,然后线程B也加入到线程A后面,tail指向线程B的结点。然后线程A和B都在其myPred域上旋转,一旦它的myPred结点的locked字段变为false,它就可以获取锁。明显线程A的myPred locked域为false,此时线程A获取到了锁。

在这里插入图片描述

public class CLHLock implements Lock {  AtomicReference<QNode> tail ;  ThreadLocal<QNode> myPred;  ThreadLocal<QNode> myNode;  public CLHLock() {  tail = new AtomicReference<QNode>(new QNode());  myNode = new ThreadLocal<QNode>() {  protected QNode initialValue() {  return new QNode();  }  };  myPred = new ThreadLocal<QNode>() {  protected QNode initialValue() {  return null;  }  };  }  @Override  public void lock() {  QNode qnode = myNode.get();  qnode.locked = true;  QNode pred = tail.getAndSet(qnode);  myPred.set(pred);  while (pred.locked) {  }  }  @Override  public void unlock() {  QNode qnode = myNode.get();  qnode.locked = false;  myNode.set(myPred.get());  }  
}

CLH分析

CLH队列锁的优点是空间复杂度低(如果有n个线程,L个锁,每个线程每次只获取一个锁,那么需要的存储空间是O(L+n),n个线程有n个myNode,L个锁有L个tail),CLH的一种变体被应用在了JAVA并发框架中(AbstractQueuedSynchronizer.Node)。CLH在SMP系统结构下该法是非常有效的。但在NUMA系统结构下,每个线程有自己的内存,如果前趋结点的内存位置比较远,自旋判断前趋结点的locked域,性能将大打折扣。

一种解决NUMA系统结构的思路是MCS队列锁

MSCCLH最大的不同并不是链表是显示还是隐式,而是线程自旋的规则不同:CLH是在前趋结点的locked域上自旋等待,而MCS是在自己的结点的locked域上自旋等待。正因为如此,它解决了CLH在NUMA系统架构中获取locked域状态内存过远的问题。
MCS队列锁的具体实现如下:

a. 队列初始化时没有结点,tail=null
b. 线程A想要获取锁,于是将自己置于队尾,由于它是第一个结点,它的locked域为false
c. 线程B和C相继加入队列,a->next=b,b->next=c。且B和C现在没有获取锁,处于等待状态,所以它们的locked域为true,尾指针指向线程C对应的结点
d. 线程A释放锁后,顺着它的next指针找到了线程B,并把B的locked域设置为false。这一动作会触发线程B获取锁

转自


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

相关文章

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

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

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

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

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

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

java中的函数式编程(一)

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

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

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

java-函数式编程浅谈

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

Java 8函数式编程

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

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

前言 &#x1f4eb;作者简介&#xff1a;小明Java问道之路&#xff0c;专注于研究计算机底层/Java/Liunx 内核&#xff0c;就职于大型金融公司后端高级工程师&#xff0c;擅长交易领域的高安全/可用/并发/性能的架构设计&#x1f4eb; &#x1f3c6;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 在最开始是不支持函数式编程的&#xff0c;想来也好理解&#xff0c;因为在 Java 中类 Class 才是第一等公民&#xff0c;这就导致在 Java 中实现编程不是件那么容易的事儿&#xff0c;不过虽然难&#xff0c;但是结果我们也已经知道了&#xff0c;在 Java 8 这个大版本里…

Java函数式编程详解

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

Java 函数式编程 详细介绍

在兼顾面向对象特性的基础上&#xff0c;Java语言通过Lambda表达式与方法引用等&#xff0c;为开发者打开了函数式编程的大门。 下面我们做一个初探。 Lambda的延迟执行 有些场景的代码执行后&#xff0c;结果不一定会被使用&#xff0c;从而造成性能浪费。而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 在最开始是不支持函数式编程的&#xff0c;想来也好理解&#xff0c;因为在 Java 中类 Class 才是第一等公民&#xff0c;这就导致在 Java 中实现编程不是件那么容易的事儿&#xff0c;不过虽然难&#xff0c;但是结果我们也已经知道了&#xff0c;在 Java 8 这个大版本里…

Oracle数据库 存储过程入门

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

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

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

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

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

数据库:存储过程实验

一、实验目的及要求 目的 掌握存储过程的编写与调用 要求 掌握存储过程的编写&#xff1b;掌握存储过程的调用 二、实验条件 安装有SQL Server2014数据库的计算机 三、实验内容 使用事务、锁和游标&#xff1b;编写和使用存储过程&#xff1b;使用触发器 四、实验结果…

达梦数据库存储过程注意事项

引言&#xff1a;达梦数据库是一款国产数据库&#xff0c;在语法使用和函数方面和MySQL&#xff0c;Oracle有着很多相似的地方。但是也有一 些细微的区别。 1、先看一下达梦数据库的存储过程模板&#xff1a; CREATE OR REPLACE FUNCTION getName() AS OR IS DECLARE ... BEGI…