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. [ucgui] 仪表盘函数

    /* 仪表盘 */ void DrawArcScale(void) { ; ; int i; ]; GUI_SetBkColor(GUI_WHITE); GUI_Clear(); GUI_SetPen ...

  2. DevOps Workshop 研发运维一体化(成都站) 2016.05.08

    成都的培训与广州.上海.北京一样,只是会议室比较小,比较拥挤,大家都将就了.可惜换了电脑以后,没有找到培训时的照片了,遗憾. 培训思路基没有太大变化,基本按照下面的思路进行: 第一天对软件开发的需求管 ...

  3. [JS] IE下ajax请求不生效或者请求结果不更新

    问题描述: IE8及以下版本里用jQuery发简单的GET时,第一次或者新开窗口后的请求没问题,可以正确返回结果.但是之后刷新页面或者触发某些操作得到的ajax请求结果永远和第一次一样. 问题分析: ...

  4. 基于VMware Workstation搭建开发服务器

    基于VMware Workstation搭建开发服务器   文章为本人原创,转载请联系作者并注明出处.晓松 源URL: https://www.jianshu.com/p/e62ab7de0124 我 ...

  5. Swagger 导出API

    Swagger 导出API 这算是在博客园的第一篇博客吧,之后发的应该也会同步到博客园上. 此前的博客地址: https://blog.mytyiluo.cn Swagger简介 Swagger是一个 ...

  6. FFmpeg软硬解和多线程解码

    一. AVCodecContext解码上下文 1.avcodec_register_all() : 注册所有的解码器 2.AVCodec *avcodec_find_decoder(enum AVCo ...

  7. jquery判断滚动到某个div显示底部按钮

    判读滚动某个div显示底部按钮 <!DOCTYPE html> <html lang="zh-CN"> <head> <meta char ...

  8. Elasticsearch分页

    Elasticsearch的数据都存在每个节点的分片中,当执行搜索时每个分片独立搜索后,数据再经过整合返回.ElasticSearch的搜索请求一次请求最大量为10000.如果超过则会发生错误.那么, ...

  9. Storm集群参数调整

    Supervisor 参数调整 修改${STORM_HOME}conf/storm.yaml文件内容 supervisor变更参数 slots 配置: 若storm host仅仅执行superviso ...

  10. [Swift实际操作]七、常见概念-(9)使用定时组件Timer执行定时任务

    本文将为你演示计时器的使用,使用计时器可以每隔一定时间执行某个函数. 在左侧的项目导航区,打开视图控制器的代码文件:ViewController.swift现在开始编写代码,实现任务定时的功能.定义一 ...