JUC锁机制
JUC锁机制(Lock)学习笔记,附详细源码解析
JUC锁机制(Lock)学习笔记,附详细源码解析
2013-08-22 20:03 by CM4J, 56 阅读, 0 评论,收藏, 编辑
锁机制学习笔记
目录:
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(); } |
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 |
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 0,代表当前锁无其他线程持有 if (isFirst(current) && compareAndSetState(0, acquires)) { // isFirst是公平锁和非公平锁在tryAcquire的唯一区别 setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { // 非0,代表有线程持有锁,判断持有者是否为当前线程 // 这里修改为旧值+1呢?这是因为ReentrantLock是可重入锁,同一个线程每持有一次就+1 int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } |
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; } |
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.自旋请求锁
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 |
final boolean acquireQueued(final Node node, int arg) {
try { boolean interrupted = false; // 循环操作 for (;;) { final Node p = node.predecessor(); // 如果第一个节点是Dummy Header也就是傀儡节点,那么第二个节点实际上就是头结点了,此时则尝试获取锁 if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC return interrupted; } // acquire失败,判断是否应该park,并检查线程是否interrupt if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } catch (RuntimeException ex) { cancelAcquire(node); throw ex; } } // CANCELLED = 1 |
IIIII.释放锁
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 |
public final boolean release(int arg) {
// tryRelease 成功 if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) // unpark节点链的head后续节点的线程 unparkSuccessor(h); return true; } return false; } protected final boolean tryRelease(int releases) { // 从头结点的下一个节点开始寻找继任节点,当且仅当继任节点的waitStatus<=0才是有效继任节点,否则将这些waitStatus>0(也就是CANCELLED的节点)从AQS队列中剔除 |
原创文章,请注明引用来源:CM4J
参考文章:http://www.blogjava.net/xylz/archive/2010/07/05/325274.html
JUC锁机制的更多相关文章
- JUC.Lock(锁机制)学习笔记[附详细源码解析]
锁机制学习笔记 目录: CAS的意义 锁的一些基本原理 ReentrantLock的相关代码结构 两个重要的状态 I.AQS的state(int类型,32位) II.Node的waitStatus 获 ...
- Java多线程系列--“JUC锁”03之 公平锁(一)
概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...
- Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock
本章对ReentrantLock包进行基本介绍,这一章主要对ReentrantLock进行概括性的介绍,内容包括:ReentrantLock介绍ReentrantLock函数列表ReentrantLo ...
- Java多线程系列--“JUC锁”01之 框架
本章,我们介绍锁的架构:后面的章节将会对它们逐个进行分析介绍.目录如下:01. Java多线程系列--“JUC锁”01之 框架02. Java多线程系列--“JUC锁”02之 互斥锁Reentrant ...
- java多线程系类:JUC锁:01之框架
本章,我们介绍锁的架构:后面的章节将会对它们逐个进行分析介绍.目录如下:01. Java多线程系列--"JUC锁"01之 框架02. Java多线程系列--"JUC锁&q ...
- Java多线程系列--“JUC锁”05之 非公平锁
概要 前面两章分析了"公平锁的获取和释放机制",这一章开始对“非公平锁”的获取锁/释放锁的过程进行分析.内容包括:参考代码获取非公平锁(基于JDK1.7.0_40)释放非公平锁(基 ...
- Java多线程系列--“JUC锁”11之 Semaphore信号量的原理和示例
概要 本章,我们对JUC包中的信号量Semaphore进行学习.内容包括:Semaphore简介Semaphore数据结构Semaphore源码分析(基于JDK1.7.0_40)Semaphore示例 ...
- 深入浅出Java并发包—锁机制(一)
前面我们看到了Lock和synchronized都能正常的保证数据的一致性(上文例子中执行的结果都是20000000),也看到了Lock的优势,那究竟他们是什么原理来保障的呢?今天我们就来探讨下Jav ...
- 转 : 深入解析Java锁机制
深入解析Java锁机制 https://mp.weixin.qq.com/s?__biz=MzU0OTE4MzYzMw%3D%3D&mid=2247485524&idx=1&s ...
随机推荐
- 浅谈 js 字符串之神奇的转义
原文:浅谈 js 字符串之神奇的转义 字符串在js里是非常常用的,但是你真的了解它么?翻阅<MDN String>就可以了解它的常见用法了,开门见山的就让你了解了字符串是怎么回事. 'st ...
- [kmp+dp] hdu 4628 Pieces
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4622 Reincarnation Time Limit: 6000/3000 MS (Java/Ot ...
- BZOJ 3282 Tree Link-Cut-Tree(LCT)
题目大意: 给定N个点以及每一个点的权值,要你处理接下来的M个操作.操作有4种.操作从0到3编号.点从1到N编号. 0:后接两个整数(x,y),代表询问从x到y的路径上的点的权值的xor和.保证x到y ...
- vi使用高级
下面的命令在vi命令模式被激活 h 光标左移一个字符 l 光标向右移动一个字符 j 光标下移一行 k 移动光标线 一.对整行操作 1.复制光标所在行 yy 2.删除光标所在行 dd 3.选中光标所在行 ...
- Coreseek:索引和检测的第二步骤施工
1,非常索引easy,代码行 g:/service/coreseek/bin/indexer -c g:/service/coreseek/etc/csft_mysql.conf person 在 ...
- Windows环境搭建与第一个C# Sample
Redis入门 - Windows环境搭建与第一个C# Sample 什么是Redis? Redis是一个开源.支持网络.基于内存.键值对存储数据库,使用ANSI C编写.从2013年5月开始,R ...
- Appium Android Bootstrap源码分析之控件AndroidElement
通过上一篇文章<Appium Android Bootstrap源码分析之简介>我们对bootstrap的定义以及其在appium和uiautomator处于一个什么样的位置有了一个初步的 ...
- ASPX的Timer位置没放正确,导致整页刷新,而不是UpdatePanel里的内容刷新。
提示:Timer应该放在UpdatePanel的ContentTemplate标签里,才行.放在外面的话,会导致整页刷新.
- DOM2级事件对象、添加事件、阻止默认事件、阻止冒泡事件、获取事件对象目标的兼容处理
事件对象——兼容处理 /* * 功能: 事件对象兼容 * 参数: 表示常规浏览器的事件对象e */ function getEvent(e) { // 如果存在e存在,直接返回,否则返回window. ...
- 快速构建Windows 8风格应用23-App Bar概述及使用规范
原文:快速构建Windows 8风格应用23-App Bar概述及使用规范 本篇博文主要介绍App Bar概述.App Bar命令组织步骤.App Bar最佳实践. App Bar概述 Windo ...