MCS锁和CLH锁
CLH锁:自旋锁,在上一个节点上等待,先上代码:
public class CLHLock {
/**
* 保证原子性操作
*
*/
private AtomicReference<Node> tail = new AtomicReference<>(new Node());
/**
* 每个线程的节点变量不同
*
*/
private ThreadLocal<Node> current = ThreadLocal.withInitial(() -> new Node());
/**
* 记录前驱节点,并且复用此节点来防止死锁:
* 假设不使用该节点的话,有T1,T2两个线程,T1先lock成功,然后T2调用lock()时会
* 自旋在T1的节点的locked字段上,如果当T1线程unlock()之后,(T2还没获取到CPU时间片),
* T1再次调用lock(),因为此时tail的值是T2线程的节点,其locked值为true,所以T1自旋等待
* T2释放锁,而此时的T2还在等T1释放锁,这就造成了死锁。
*
*/
private ThreadLocal<Node> pred = new ThreadLocal<>(); public void lock() {
Node node = current.get();
node.locked = true;
// 将tail设置为当前线程的节点,并获取到上一个节点,此操作为原子性操作
Node preNode = tail.getAndSet(node);
pred.set(preNode);
// 在前驱节点的locked字段上忙等待
while (preNode.locked); } public void unlock() {
Node node = current.get();
// 将当前线程节点的locked属性设置为false,使下一个节点成功获取锁
node.locked = false;
current.set(pred.get());
} static class Node{
volatile boolean locked;
}
}
注意它的实例变量,tail为一个原子引用,所以在它上的操作都是原子性操作,它是所有线程共享的变量,与后面的两个变量区分开,current是线程本地变量,它的值都和当前线程有关。current记录的是当前线程的锁情况。
加锁时,现将current的locked属性设置为true,表示当前线程需要获取锁,然后将tail中的值设置为当前线程节点,getAndSet方法设置新值并返回之前的值,这样每个线程节点之间就有一条隐形的”链“关联着,像一个链表。最后在上一个节点的locked属性上自旋等待。
解锁时,只需把当前节点的locked属性设置为false,这样紧接着的后面一个的线程就会成功的获取锁。
MCS锁:
public class MCSLock {
AtomicReference<Node> tail = new AtomicReference<>();
ThreadLocal<Node> current = ThreadLocal.withInitial(() -> new Node()); public void lock() {
Node node = current.get();
Node pred = tail.getAndSet(node);
// pred的初始值为null,所以第一个加锁线程,直接跳过判断,加锁成功
// tail中记录的是当前线程的节点
if (pred != null) {
pred.next = node;
while (node.locked);
} } public void unlock() {
Node node = current.get();
if (node.next == null) {
// 如果设置成功,说明在此之前没有线程进行lock操作,直接return即可;
// 如果失败,则说明在此之前有线程进行lock操作,需要自旋等待那个线程将自身节点设置为本线程节点的next,
// 然后进行后面的操作。
if (tail.compareAndSet(node, null))
return;
while (node.next == null);
}
// 通知下一个线程,使下一个线程加锁成功
node.next.locked = false;
// 解锁后需要将节点之间的关联断开,否则会产生内存泄露
node.next = null;
} static class Node{
volatile boolean locked = true;
volatile Node next;
} }
CLH和MCS锁都是自旋锁,公平锁(保证FIFO),独占锁,并且是不可重入锁。他们两的名字都是发明者的名字的缩写。
MCS锁和CLH锁的更多相关文章
- 可重入锁 公平锁 读写锁、CLH队列、CLH队列锁、自旋锁、排队自旋锁、MCS锁、CLH锁
1.可重入锁 如果锁具备可重入性,则称作为可重入锁. ========================================== (转)可重入和不可重入 2011-10-04 21:38 这 ...
- 自旋锁、排队自旋锁、MCS锁、CLH锁
转载自:http://coderbee.net/index.php/concurrent/20131115/577 自旋锁(Spin lock) 自旋锁是指当一个线程尝试获取某个锁时,如果该锁已被其他 ...
- CLH锁 、MCS锁
一.引文 1.1 SMP(Symmetric Multi-Processor) 对称多处理器结构,指服务器中多个CPU对称工作,每个CPU访问内存地址所需时间相同.其主要特征是共享,包含对CPU,内存 ...
- Java并发框架——AQS阻塞队列管理(三)——CLH锁改造
在CLH锁核心思想的影响下,Java并发包的基础框架AQS以CLH锁作为基础而设计,其中主要是考虑到CLH锁更容易实现取消与超时功能.比起原来的CLH锁已经做了很大的改造,主要从两方面进行了改造:节点 ...
- 并发编程——详解 AQS CLH 锁
从 acquire 方法开始 -- 获取 为什么 AQS 需要一个虚拟 head 节点 reelase 方法如何释放锁 总结 前言 AQS 是 JUC 中的核心,其中封装了资源的获取和释放,在我们之前 ...
- JAVA 并发:CLH 锁 与 AbstractQueuedSynchronizer
首先向Doug Lea致敬. CLH 以下是CLH锁的一个简单实现: class SimpleCLHLock { /** * initialized with a dummy node */ priv ...
- Java锁之自旋锁详解
锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) .这些已经写好提供的锁为我们开发提供了便利,但是锁的具体性质以及类 ...
- JUC 并发编程--12, 使用AtomicInteger 实现一把锁(排队自旋锁), 代码演示
前面 使用自旋锁实现了一把锁,(请看 第5篇) volatile 三大特性: 可见性, 不保证原子性, 禁止指令重排 为了解决 volatile不保证原子性的问题, 引入了原子类, AtomicInt ...
- Java锁--非公平锁
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3496651.html 参考代码 下面给出Java1.7.0_40版本中,ReentrantLock和AQ ...
随机推荐
- 查看sql server数据库连接数的三种方法
怎样才能查看sql server数据库连接数呢?下面就将为您介绍三种查看的方法,供您参考,希望能够帮助到您. 1.通过系统的“性能”来查看:开始->管理工具->性能(或者是运行里面输入 m ...
- C++ - explicit和volatile/const的内容
第一眼见到explicit和volatile可能会一愣一愣的觉得可能是c11或者c14新加的标识符. 其实不是这样,volatile和const两个关键字在C语言的第二个版本KR C的时候就被加入了C ...
- linux系统编程之进程(三):进程复制fork,孤儿进程,僵尸进程
本节目标: 复制进程映像 fork系统调用 孤儿进程.僵尸进程 写时复制 一,进程复制(或产生) 使用fork函数得到的子进程从父进程的继承了整个进程的地址空间,包括:进程上下文.进程堆栈. ...
- ASP.Net Core 2.2 MVC入门到基本使用系列 (一)
本教程会对基本的.Net Core 进行一个大概的且不会太深入的讲解, 在您看完本系列之后, 能基本甚至熟练的使用.Net Core进行Web开发, 感受到.Net Core的魅力. 本教程知识点大体 ...
- C# 32位程序,申请大内存,附dome(wpf),亲测可用
1.我是vs2017,在选装vs的时候,需要安装c++模块,因为申请大内存的必要exe存放在vc的某个目录(下面会给出详细的地址)下的 2.安装完成在vs的安装目录可找到这个文件,我是社区版本的,如果 ...
- Text and Binary modes
http://perlmaven.com/what-is-a-text-file https://cygwin.com/cygwin-ug-net/using-textbinary.html Text ...
- FunDA(11)- 数据库操作的并行运算:Parallel data processing
FunDA最重要的设计目标之一就是能够实现数据库操作的并行运算.我们先重温一下fs2是如何实现并行运算的.我们用interleave.merge.either这几种方式来同时处理两个Stream里的元 ...
- servlet转发与重定向区别
servlet转发与重定向 HttpServletRequest: 表示浏览器向服务器发送的请求报文,由Tomcat创建然后作为参数传递给我们doget,dopost方法. 功能:getParam ...
- C# 程序运行中的流程控制
1.C#之流程控制语句:计算机程序执行的控制流程由三种基本的控制结构控制,即顺序结构,选择结构,循环结构. 1) 顺序结构:从上到下,按照书写顺序执行每一条语句,不会发生跳跃. 代码段1; // 先执 ...
- JavaScript父子页面之间的相互调用
父页面: <!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head>< ...