从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),具体的数据库连接实 ...
随机推荐
- Redis Sentinel主从高可用方案
Redis Sentinel主从高可用方案 本文介绍一种通过Jed和Sentinel实现Redis集群(主从)的高可用方案,该方案需要使用Jedis2.2.2及以上版本(强制),Redis2.8及以上 ...
- 使用svn无法cleanup和lock问题
step1: 到 sqlite官网 (http://www.sqlite.org/download.html) 下载 sqlite3.exe 找到 Precompiled Binaries for W ...
- Java常用排序算法
在排序过程中,全部记录存放在内存,则称为内排序,如果排序过程中需要使用外存,则称为外排序. 一般来说外排序分为两个步骤:预处理和合并排序.首先,根据可用内存的大小,将外存上含有n个纪录的文件分成若干长 ...
- JAVA局部内部类
在刚刚学到的android开发中了解到Button的onClick是通过局部内部类的方式实现的,具体的原理我以后再去了解,只是遇到问题总是想知道为什么,不要告诉我这是规则,死记住就可以了. 问题是局部 ...
- Java 零散笔记
运算符: 整数被0除将会产生一个异常,而浮点数被0除会得到无穷大或NaN结果. 二元运算符: 如果运算符得到一个值,其类型与左侧操作数的类型不用,就会发生强制类型转换. int x = 0; x += ...
- Spark Streaming中的操作函数分析
根据Spark官方文档中的描述,在Spark Streaming应用中,一个DStream对象可以调用多种操作,主要分为以下几类 Transformations Window Operations J ...
- 微信开发之使用java获取签名signature(贴源码,附工程)
一.前言 微信接口调用验证最终需要用到的三个参数noncestr.timestamp.signature: 接下来将会给出获取这三个参数的详细代码 本文的环境eclipse + maven 本文使用到 ...
- Maven简介(Maven是什么)
简介 Maven,在意第绪语中意为对知识的积累.Maven最初用来在Jakarta Turbine项目中简化该项目的构建过程.Jakarta Trubine项目有多个工程,每个工程都有自己的多个Ant ...
- Dynamics CRM2016 Web API之删除
相比之前的增改查,删除就显得简单的多了. 这里的request的type为delete,删除成功的status为204,404则是要删除的记录不存在 var id = 'BAD90A95-7FEA-E ...
- Erlang的常驻模块与功能模块
Erlang的常驻模块与功能模块Residence moduleThe module where a process has its tail-recursive loop function(s).I ...