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锁的更多相关文章

  1. 可重入锁 公平锁 读写锁、CLH队列、CLH队列锁、自旋锁、排队自旋锁、MCS锁、CLH锁

    1.可重入锁 如果锁具备可重入性,则称作为可重入锁. ========================================== (转)可重入和不可重入 2011-10-04 21:38 这 ...

  2. 自旋锁、排队自旋锁、MCS锁、CLH锁

    转载自:http://coderbee.net/index.php/concurrent/20131115/577 自旋锁(Spin lock) 自旋锁是指当一个线程尝试获取某个锁时,如果该锁已被其他 ...

  3. CLH锁 、MCS锁

    一.引文 1.1 SMP(Symmetric Multi-Processor) 对称多处理器结构,指服务器中多个CPU对称工作,每个CPU访问内存地址所需时间相同.其主要特征是共享,包含对CPU,内存 ...

  4. Java并发框架——AQS阻塞队列管理(三)——CLH锁改造

    在CLH锁核心思想的影响下,Java并发包的基础框架AQS以CLH锁作为基础而设计,其中主要是考虑到CLH锁更容易实现取消与超时功能.比起原来的CLH锁已经做了很大的改造,主要从两方面进行了改造:节点 ...

  5. 并发编程——详解 AQS CLH 锁

    从 acquire 方法开始 -- 获取 为什么 AQS 需要一个虚拟 head 节点 reelase 方法如何释放锁 总结 前言 AQS 是 JUC 中的核心,其中封装了资源的获取和释放,在我们之前 ...

  6. JAVA 并发:CLH 锁 与 AbstractQueuedSynchronizer

    首先向Doug Lea致敬. CLH 以下是CLH锁的一个简单实现: class SimpleCLHLock { /** * initialized with a dummy node */ priv ...

  7. Java锁之自旋锁详解

    锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) .这些已经写好提供的锁为我们开发提供了便利,但是锁的具体性质以及类 ...

  8. JUC 并发编程--12, 使用AtomicInteger 实现一把锁(排队自旋锁), 代码演示

    前面 使用自旋锁实现了一把锁,(请看 第5篇) volatile 三大特性: 可见性, 不保证原子性, 禁止指令重排 为了解决 volatile不保证原子性的问题, 引入了原子类, AtomicInt ...

  9. Java锁--非公平锁

    转载请注明出处:http://www.cnblogs.com/skywang12345/p/3496651.html 参考代码 下面给出Java1.7.0_40版本中,ReentrantLock和AQ ...

随机推荐

  1. mac下的抓包工具 -- Charles

    # 背景 换了mac电脑,

  2. image的关闭和开启

    image1.gameObject.SetActive(true);开启image,image1.gameObject.SetActive(false);关闭image,可以用事件或者button开启 ...

  3. C#字符串转UNICODE

    public static string StringToUnicode(string s)//字符串转UNICODE代码 { char[] charbuffers = s.ToCharArray() ...

  4. 文本比较算法Ⅱ——Needleman/Wunsch算法的C++实现【求最长公共子串(不需要连续)】

    算法见:http://www.cnblogs.com/grenet/archive/2010/06/03/1750454.html 求最长公共子串(不需要连续) #include <stdio. ...

  5. Design-341. Flatten Nested List Iterator

    Given a nested list of integers, implement an iterator to flatten it. Each element is either an inte ...

  6. (3)视觉里程计 Visual Odometry

    首先分析include头文件下的slamBase.h文件 # pragma once // 各种头文件 // C++标准库 #include <fstream> #include < ...

  7. C# - 反射与编译

    编译 首先了解下,如何区分编译生成的 .dll的版本方法1:ILSpy反编译工具 通过 assembly属性,release版本没有或仅有如下一种属性 [assembly: Debuggable(De ...

  8. http2.0之头部压缩

    什么是头部压缩?为什么要头部压缩? 我们知道,http请求和响应都是由[状态行.请求/响应头部.消息主题]三部分组成的. 一般而言,消息主体都会经过gzip压缩,或者本身传输的就是压缩过后的二进制文件 ...

  9. 现代cpu的合并写技术对程序的影响

    对于现代cpu而言,性能瓶颈则是对于内存的访问.cpu的速度往往都比主存的高至少两个数量级.因此cpu都引入了L1_cache与L2_cache,更加高端的cpu还加入了L3_cache.很显然,这个 ...

  10. python中的sort方法

    Python中的sort()方法用于数组排序,本文以实例形式对此加以详细说明: 一.基本形式 列表有自己的sort方法,其对列表进行原址排序,既然是原址排序,那显然元组不可能拥有这种方法,因为元组是不 ...