JUC.Lock(锁机制)学习笔记[附详细源码解析]
锁机制学习笔记
目录:
JUC的并发包功能强大,但也不容易理解,大神果然是用来膜拜的。经过一段时间的研究和理解,我把自己所了解的关于JUC中锁的相关知识整理下来,一方面给自己做个备忘,另一方面也给各位朋友做个参考。
文中源码的关键部分都做了注释,希望对大家有所帮助。另外这只是学习笔记,建议大家先去了解一些基础知识再来看其中的源码,大家有疑问的可以再参考其他文章,谢谢!
CAS的意义
锁的一些基本原理
ReentrantLock的相关代码结构

两个重要的状态
I.AQS的state(int类型,32位)
II.Node的waitStatus
对队列中节点的操作(锁定线程或释放线程)则是基于节点的waitStatus的
CANCELLED = 1:
节点操作因为超时或者对应的线程被interrupt。节点不应该不留在此状态,一旦达到此状态将从CHL队列中踢出。
SIGNAL =
-1:
节点的继任节点是(或者将要成为)BLOCKED状态(例如通过LockSupport.park()操作),因此一个节点一旦被释放(解锁)或者取消就需要唤醒(LockSupport.unpack())它的继任节点。
CONDITION = -2:
表明节点对应的线程因为不满足一个条件(Condition)而被阻塞。
正常状态 =
0:
新生的非CONDITION节点都是此状态。
对于处在阻塞队列中的节点,当前节点之前的节点:
waitStatus >
0的是取消的节点,在处理中应该剔除
waitStatus = 0的,则需要将其改成-1
因此整个阻塞节点链的waitStatus应该为:-1,-1,-1,0
获取锁(AQS)的流程
锁的获取和释放都是基于上述2个状态来的,首先能不能获取锁是由AQS.state来控制,因此tryAcquire()和tryRelease()都是对state的控制,如果不能获取锁则需要加入到等待队列,此时线程的等待与释放则是由Node的waitStatus控制的。
下图演示了一个线程获取独占锁的过程:


I.获取锁总操作
1
2 3 4 |
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } |
tryAcquire(arg):
2.
addWaiter(Node.EXCLUSIVE):
3.
acquireQueued(addWaiter(Node.EXCLUSIVE), arg):
4.
selfInterrupt():
II.tryAcquire(尝试获取锁)
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
)
throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } |
isFirst(current),它的作用就是判断AQS是否为空或者当前线程是否在队列头
III.添加到等待队列

1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode); // 这一段是为提高性能而设的,没有也不影响功能 // 如果tail不为空,则设置新tail并返回 Node pred = tail; if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } enq(node); // 如果tail为空,则执行enq(),创建新head,插入队列,否则逻辑和上面一样 return node; } |
Code
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
private Node enq(final Node node) {
for (;;) { Node t = tail; if (t == null) { // tail == null,创建新的节点加入到尾部 Node h = new Node(); // dummy header,傀儡节点 h.next = node; node.prev = h; if (compareAndSetHead(h)) { // CAS设置头部 tail = node; return h; } } else { // tail != null,则和addWaiter()中那段一样 node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } } |
IIII.自旋请求锁
Code
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
);
pred.next = node; } else { // waitStatus=0,修改前一个节点状态位为SINGAL,表示后面有节点等待你处理,需要根据它的等待状态来决定是否该park() compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } // ws<0才需要park(),ws>=0都返回false,表示线程不应该park() return false; } |
IIIII.释放锁
Code
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
)
s = t; } if (s != null) LockSupport.unpark(s.thread); } |
原创文章,请注明引用来源:CM4J
参考文章:http://www.blogjava.net/xylz/archive/2010/07/05/325274.html
JUC.Lock(锁机制)学习笔记[附详细源码解析]的更多相关文章
- JUC.Condition学习笔记[附详细源码解析]
目录 Condition的概念 大体实现流程 I.初始化状态 II.await()操作 III.signal()操作 3个主要方法 Condition的数据结构 线程何时阻塞和释放 await()方法 ...
- Laravel学习笔记之Session源码解析(上)
说明:本文主要通过学习Laravel的session源码学习Laravel是如何设计session的,将自己的学习心得分享出来,希望对别人有所帮助.Laravel在web middleware中定义了 ...
- Laravel学习笔记之Session源码解析(下)
说明:在中篇中学习了session的CRUD增删改查操作,本篇主要学习关闭session的相关源码.实际上,在Laravel5.3中关闭session主要包括两个过程:保存当前URL到session介 ...
- Laravel学习笔记之Session源码解析(中)
说明:在上篇中学习了session的启动过程,主要分为两步,一是session的实例化,即\Illuminate\Session\Store的实例化:二是从session存储介质redis中读取id ...
- memcached学习笔记——存储命令源码分析上篇
原创文章,转载请标明,谢谢. 上一篇分析过memcached的连接模型,了解memcached是如何高效处理客户端连接,这一篇分析memcached源码中的process_update_command ...
- memcached学习笔记——存储命令源码分析下篇
上一篇回顾:<memcached学习笔记——存储命令源码分析上篇>通过分析memcached的存储命令源码的过程,了解了memcached如何解析文本命令和mencached的内存管理机制 ...
- Hadoop学习笔记(10) ——搭建源码学习环境
Hadoop学习笔记(10) ——搭建源码学习环境 上一章中,我们对整个hadoop的目录及源码目录有了一个初步的了解,接下来计划深入学习一下这头神象作品了.但是看代码用什么,难不成gedit?,单步 ...
- [Spark內核] 第42课:Spark Broadcast内幕解密:Broadcast运行机制彻底解密、Broadcast源码解析、Broadcast最佳实践
本课主题 Broadcast 运行原理图 Broadcast 源码解析 Broadcast 运行原理图 Broadcast 就是将数据从一个节点发送到其他的节点上; 例如 Driver 上有一张表,而 ...
- Sping学习笔记(一)----Spring源码阅读环境的搭建
idea搭建spring源码阅读环境 安装gradle Github下载Spring源码 新建学习spring源码的项目 idea搭建spring源码阅读环境 安装gradle 在官网中下载gradl ...
随机推荐
- 怎样用vs 2008 编译C程序
在大学里都习惯了vc6.0,但是它的集成度并不好,刚试着用vs2008,简单说一下用vs2008编译c的方法吧 首先,在file中新建工程project ,确定之后,会出现新工程(new projec ...
- OpenStack Mitaka安装
http://egon09.blog.51cto.com/9161406/1839667 前言: openstack的部署非常简单,简单的前提建立在扎实的理论功底,本人一直觉得,玩技术一定是理论指导实 ...
- 好的bootstrap文章
http://www.cnblogs.com/gamehiboy/p/5176618.html http://www.cnblogs.com/landeanfen/p/5821192.html htt ...
- JVM内存模型和性能优化 转
JVM内存模型和性能优化 JVM内存模型优点 内置基于内存的并发模型: 多线程机制 同步锁Synchronization 大量线程安全型库包支持 基于内存的并发机制,粒度灵活控制,灵活度高于 ...
- winform中生成TreeView树
无论是webform还是winform,TreeView都是常用功能.使用递归方法很方便. 下面分享一个小实例. 数据库中3个字段,分别是:ID,itemType_name,itemType_PID ...
- php ffmpeg
可以合成视频,音频视频合成等 $aurl = '../Uploads/movie/zzz.mp4'; $vurl = '../Uploads/mp3/dmd.mp3'; // exec("c ...
- Keep-Alive 总结
一.简介 Keep-Alive是http请求头信息中的一个配置参数,开启之后俗称保持HTTP长连接,HTTP 1.0和HTTP 1.1都支持,HTTP1.0中该属性默认是关闭的,可在头信息中配置开启( ...
- 服务端性能测试校准v1.2
服务端性能测试工具校验v1.2 想知道压力工具实际并发多少,想知道压力工具统计响应数据准不准,来试试这款校准工具. 更新说明: 1.修正总接收请求显示上限. 2.随着响应时间增加,自动增加处理线程. ...
- Install NukeX v7.0v6 in CentOS 7
- download THE_FOUNDRY_NUKEX_V7.0V6_LNX64-XFORCE - unzip and untar to /home/user0/tools/foundry/nuke ...
- Windows安装Node.Js
1.下载https://nodejs.org/ 2.安装 3.修改环境变量,添加安装的Node.js的目录(此处似乎可以不用配置) 4.查看npm指令 关于npm介绍“Nodejs自身提供了基本的模块 ...