从JDK源码角度看并发锁的优化
在CLH锁核心思想的影响下,JDK并发包以CLH锁作为基础而设计,其中主要是考虑到CLH锁更容易实现取消与超时功能。比起原来的CLH锁已经做了很大的改造,主要从两方面进行了改造:节点的结构与节点等待机制。在结构上引入了头结点和尾节点,他们分别指向队列的头和尾,尝试获取锁、入队列、释放锁等实现都与头尾节点相关,并且每个节点都引入前驱节点和后后续节点的引用;在等待机制上由原来的自旋改成阻塞唤醒。如图,通过前驱后续节点的引用一节节连接起来形成一个链表队列,对于头尾节点的更新必须是原子的。下面详细看看入队、检测挂起、释放出队、超时、取消等操作。
① 入队,整块逻辑其实是用一个无限循环进行CAS操作,即用自旋方式竞争直到成功。将尾节点tail的旧值赋予新节点node的前驱节点,并尝试CAS操作将新节点node赋予尾节点tail,原先的尾节点的后续节点指向新建节点node。完成上面步骤就建立起一条如图所示的链表队列。代码简化如下:
for (;;) { Node t = tail; node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return node; } }
② 检测挂起,上面我们说到节点等待机制已经被JDK并发作者由自旋机制改造成阻塞机制,一个新建的节点完成入队操作后,如果是自旋则直接进入循环检测前驱节点是否为头结点即可,但现在被改为阻塞机制,当前线程将首先检测是否为头结点且尝试获取锁,如果当前节点为头结点并成功获取锁则直接返回,当前线程不进入阻塞,否则将当前线程阻塞。代码简化如下:
for (;;) { if (node.prev == head) if(尝试获取锁成功){ head=node; node.next=null; return; } 阻塞线程 }
③ 释放出队,出队的主要工作是负责唤醒等待队列中后续节点,让所有等待节点环环相接,每条线程有序地往下执行。代码简化如下:
Node s = node.next;
唤醒节点s包含的线程
④ 超时,在支持超时的模式下需要LockSupport类的parkNanos方法支持,线程在阻塞一段时间后会自动唤醒,每次循环将累加消耗时间,当总消耗时间大于等于自定义的超时时间时就直接分返。代码简化如下:
for (;;) { 尝试获取锁 if (nanosTimeout <= 总消耗时间) return; LockSupport.parkNanos(this, nanosTimeout); }
⑤ 取消,队列中等待锁的队列可能因为中断或超时而涉及到取消操作,这种情况下被取消的节点不再进行锁竞争。此过程主要完成的工作是将取消的节点移除,先将节点的。先将节点node状态设置成取消,再将前驱节点pred的后续节点指向node的后续节点,这里由于涉及到竞争,必须通过CAS进行操作,CAS操作就算失败也不必理会,因为已经改了节点的状态,在尝试获取锁操作中会循环对节点的状态判断。
node.waitStatus = Node.CANCELLED; Node pred = node.prev; Node predNext = pred.next; Node next = node.next; compareAndSetNext(pred, predNext, next);
喜欢研究java的同学可以交个朋友:
从JDK源码角度看并发锁的优化的更多相关文章
- 从JDK源码角度看并发竞争的超时
JDK中的并发框架提供的另外一个优秀机制是锁获取超时的支持,当大量线程对某一锁竞争时可能导致某些线程在很长一段时间都获取不了锁,在某些场景下可能希望如果线程在一段时间内不能成功获取锁就取消对该锁的等待 ...
- 从JDK源码角度看Short
概况 Java的Short类主要的作用就是对基本类型short进行封装,提供了一些处理short类型的方法,比如short到String类型的转换方法或String类型到short类型的转换方法,当然 ...
- 从JDK源码角度看Byte
Java的Byte类主要的作用就是对基本类型byte进行封装,提供了一些处理byte类型的方法,比如byte到String类型的转换方法或String类型到byte类型的转换方法,当然也包含与其他类型 ...
- 从JDK源码角度看Object
Java的Object是所有其他类的父类,从继承的层次来看它就是最顶层根,所以它也是唯一一个没有父类的类.它包含了对象常用的一些方法,比如getClass.hashCode.equals.clone. ...
- 从JDK源码角度看Boolean
Java的Boolean类主要作用就是对基本类型boolean进行封装,提供了一些处理boolean类型的方法,比如String类型和boolean类型的转换. 主要实现源码如下: public fi ...
- 从JDK源码角度看java并发的公平性
JAVA为简化开发者开发提供了很多并发的工具,包括各种同步器,有了JDK我们只要学会简单使用类API即可.但这并不意味着不需要探索其具体的实现机制,本文从JDK源码角度简单讲讲并发时线程竞争的公平性. ...
- 从JDK源码角度看java并发的原子性如何保证
JDK源码中,在研究AQS框架时,会发现很多地方都使用了CAS操作,在并发实现中CAS操作必须具备原子性,而且是硬件级别的原子性,java被隔离在硬件之上,明显力不从心,这时为了能直接操作操作系统层面 ...
- 从JDK源码角度看java并发线程的中断
线程的定义给我们提供了并发执行多个任务的方式,大多数情况下我们会让每个任务都自行执行结束,这样能保证事务的一致性,但是有时我们希望在任务执行中取消任务,使线程停止.在java中要让线程安全.快速.可靠 ...
- 从JDK源码角度看线程池原理
"池"技术对我们来说是非常熟悉的一个概念,它的引入是为了在某些场景下提高系统某些关键节点性能,最典型的例子就是数据库连接池,JDBC是一种服务供应接口(SPI),具体的数据库连接实 ...
随机推荐
- Docker 备份、恢复、迁移数据卷
可以利用数据卷对其中的数据进行进行备份.恢复和迁移. 备份 首先使用 --volumes-from 标记来创建一个加载 dbdata 容器卷的容器,并从本地主机挂载当前到容器的 /backup 目录. ...
- Bootstrap3 代码-代码块
多行代码可以使用 <pre> 标签.为了正确的展示代码,注意将尖括号做转义处理. <p>Sample text here...</p> <pre>< ...
- TV Metro界面(仿泰捷视频TV版)源码解析
转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52822499 前言:上一篇介绍了 ...
- sum,filter和map参数里面的玄机
首先是sum函数. 最常见的用法似乎是: >>> sum([1,2,3]) 6 但其实这是默认首个元素是数字0.我们可以指定其他数字: >>> sum([1,2,3 ...
- Linux下文件和文件夹操作命令详解
花了两个小时的时间,把文件和文件夹相关的常用命令:创建.删除.移动.复制.查找.重命名在linux上测试了一把,总结下来.文件夹的这些基本操作是要多注意的,一不小心就达不到你想要的效果. 文件夹操作: ...
- Dynamics CRM2016 升级老版本报“JavaScript Web 资源包含对 Microsoft Dynamics CRM 4.0 (2007) Web 服务终结点的引用”问题的解决办法
今天在新的服务器上部署了CRM2016 on-premises,并将CRM2015的数据库拷贝过来准备附加后升级,但在升级过程中遇到了如下错误,向导检测到了我的JavaScript Web 资源中包含 ...
- JAVA面向对象-----构造方法
我们人出生的时候,有些人一出生之后再起名字的,但是有些人一旦出生就已经起好名字的.那么我们在java里面怎么在对象一旦创建就赋值呢? 构造方法作用 构造方法作用:对对象进行初始化. 构造函数与普通的函 ...
- Eclipse打jar包,资源文件的读取
最近的工作中需要将java程序打一个jar包,然后在Linux中供调用.程序中需要读取一个配置文件.遇到了三个问题.第一个是依赖的第三方Jar包打成Jar包后找不到:第二个问题是资源文件所在的文件夹打 ...
- iOS中 CocoaPods Mac App的安装和使用 韩俊强的博客
CocoaPods Mac App的安装和使用 CocoaPods桌面应用版下载地址:https://cocoapods.org/app打开应用会提示你是否安装命令行工具,选择install就也可以在 ...
- Android仿淘宝购物车demo
夏的热情渐渐退去,秋如期而至,丰收的季节,小编继续着实习之路,走着走着,就走到了购物车,逛过淘宝或者是京东的小伙伴都知道购物车里面的宝贝可不止一件,对于爱购物的姑娘来说,购物车里面的商品恐怕是爆满,添 ...