ReentrantLock分析
主要分析下ReentrantLock锁的占用和释放过程。
一.几个核心变量
AbstractOwnableSynchronizer{
/**
* 表示当前占有独占锁的线程,为null时说明锁未被占用
*/
private transient Thread exclusiveOwnerThread;
}
AbstractQueuedSynchronizer extend AbstractOwnableSynchronizer{
private transient volatile Node head;//队列首节点
private transient volatile Node tail;//队列尾节点
private volatile int state;//同步状态,表示锁是否被占用。可重入锁,占用锁时继续获取锁,state=2
}
/**
* waitStatus:
*1:线程取消等待
*-1:后继节点的线程处于等待状态,需要当前结点唤醒
*-2:等待condition,condition.signale()唤醒,该线程会加入到队列中等待获取锁
*-3:下一次共享式同步状态获取将会被无条件地传播下去??没看懂
*/
static final class Node {
volatile int waitStatus;//当前线程的等待状态。状态被一个线程修改后,立即对其他线程可见
volatile Node prev;//前置节点
volatile Node next;//后置节点
volatile Thread thread;//节点所属线程
}
AbstractQueuedSynchronizer同步控制核心类,核心变量为state,state=0表示当前锁被占用,state!=0表示锁被占用,exclusiveOwnerThread变量表示当前占用锁的线程,若为null,表示锁未被占用。
二.线程获取锁的流程
1.尝试获取锁,若获取失败,则添加到待占用锁队列,中断当前线程等待占有锁后继续运行
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
2.获取失败的锁加入等待队列,第一个节点Node0为头节点,第二个节点Node1才是链表第一个个数据节点,存储有效数据信息。

private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
//尾节点存在,即队列不为空。新节点作为新的尾节点
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
//死循环添加节点,返回node的前置节点
private Node enq(final Node node) {
for (;;) {
Node t = tail;
//尾节点不存在,初始化。设置一个前置节点node0,即为头节点也是尾节点
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
//尾节点存在,即链表有效,将新node添加到尾部
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
3.死循环获取锁。死循环,只有前置节点为头节点的链表节点,即链表的第一个数据节点尝试获取锁

final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
//前置结点为头结点,即node为第一个,设置当前节点为头节点
if (p == head && tryAcquire(arg)) {
//将当前节点设置为头结点,移除之前的头节点
setHead(node);
p.next = null; // help GC。。p节点断开和链表的连接
failed = false;
return interrupted;
}
//前置节点非首节点,则当前线程中断
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())//阻塞线程并判断线程是否中断
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
4.获取锁的具体流程
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//当前锁未被占用,且当前线程是队列中头元素Node1,如果是的话,则获取该锁,设置锁的状态,并设置锁的拥有者为当前线程
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//若当前线程占有锁,锁可重入,state+1.
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
三.释放锁的流程
1.尝试释放锁,锁释放成功,则唤醒下一个节点
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
//若锁释放成功,则唤醒当前结点的后继结点
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
2.如何释放锁
每次释放state-1,并更新线程state值,直到state减到0,该线程释放锁成功。并将exclusiveOwnerThread字段置为null,表示当前未有线程占有锁
protected final boolean tryRelease(int releases) {
//获取当前的state值,state-1
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
3.如何唤醒下一个结点
private void unparkSuccessor(Node node) {
//当前结点的等待状态为置为0,Node1
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
//从尾结点向前查找第一个waitStatus小于0的Node,Node2
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
//唤醒结点
if (s != null)
LockSupport.unpark(s.thread);
}
四.公平锁和非公平锁
ReentrantLock有可以作为公平锁和非公平锁。默认非公平锁。
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
公平ReentrantLock锁获取锁
final void lock() {
acquire(1);
}
非公平ReentrantLock锁获取锁
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
二者的区别就是非公平锁获取锁的时候首先判断锁是否被占用,若没有被占用,直接占有锁,否则加入等待队列。
公平锁获取锁的时候的直接加入等待队列。等待队列的线程满足FIFO的条件,即先进入队列的线程会先获取锁。
ReentrantLock分析的更多相关文章
- ReentrantLock 分析
带着疑问去分析 ReentrantLock是如何实现锁管理的. ReentrantLock是如何实现重入的. ReentrantLock是如何实现公平锁与非公平锁. ReentantLock的公平锁为 ...
- 透过 ReentrantLock 分析 AQS 的实现原理
对于 Java 开发者来说,都会碰到多线程访问公共资源的情况,这时候,往往都是通过加锁来保证访问资源结果的正确性.在 java 中通常采用下面两种方式来解决加锁得问题: synchronized 关键 ...
- java源码-ReentrantLock源码分析-2
继续上篇ReentrantLock分析如何唤醒线程: 当调用lock.unlock()方法最终调用AQS类中的release方法,开始释放锁 tryRelease(1)方法在Sync对象中实现,首先会 ...
- ReentrantLock和condition源码浅析(一)
转载请注明出处..... 一.介绍 大家都知道,在java中如果要对一段代码做线程安全操作,都用到了锁,当然锁的实现很多,用的比较多的是sysnchronize和reentrantLock,前者是ja ...
- JUC源码学习笔记1——AQS和ReentrantLock
笔记主要参考<Java并发编程的艺术>并且基于JDK1.8的源码进行的刨析,此篇只分析独占模式,后续在ReentrantReadWriteLock和 CountDownLatch中 会重点 ...
- java总结
JUC概况 以下是Java JUC包的主体结构: ? Atomic : AtomicInteger ? Locks : Lock, Condition, ReadWriteLock ? Collect ...
- Android 技能图谱学习路线
这里是在网上找到的一片Android学习路线,希望记录下来供以后学习 1Java 基础 Java Object类方法 HashMap原理,Hash冲突,并发集合,线程安全集合及实现原理 HashMap ...
- ThreadPoolExcutor 原理探究
概论 线程池(英语:thread pool):一种线程使用模式.线程过多会带来调度开销,进而影响缓存局部性和整体性能.而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务.这避免了在处理短时间 ...
- 深入剖析AQS
目录 摘要 AbstractQueuedSynchronizer实现一把锁 ReentrantLock ReentrantLock的特点 Synchronized的基础用法 ReentrantLock ...
随机推荐
- Mysql索引进阶入门
1. 索引操作 MySQL 索引 菜鸟 2. 索引类型 PRIMARY 唯一且不能为空:一张表只能有一个主键索引 INDEX 普通索引 UNIQUE 唯一性索引 FULLTEXT 全文索引:用于搜索很 ...
- 机器学习中的误差 Where does error come from?
误差来自于偏差和方差(bias and variance) 对于随机变量 X,假设其期望和方差分别为 μ 和 σ2.随机采样 N 个随机变量构成样本,计算算术平均值 m,并不会直接得到 μ (除非 ...
- android——卡片式布局
一.CardView <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk ...
- JVM调优之经验
在生产系统中,高吞吐和低延迟一直都是JVM调优的最终目标,但这两者恰恰又是相悖的,鱼和熊掌不可兼得,所以在调优之前要清楚舍谁而取谁.一般计算任务和组件服务会偏向高吞吐,而web展示则偏向低延迟才会带来 ...
- ABP 配置全局数据过滤器
ABP官方数据过滤的地址:https://aspnetboilerplate.com/Pages/Documents/Data-Filters 中文可以看这个:https://aspnetboiler ...
- vue 使用gojs绘制简单的流程图
在vue项目中需要展示工作流进度,可以使用的流程图插件很多 flowchart.js http://adrai.github.io/flowchart.js/ , 基于SVG创建Flow Chart ...
- python webdriver 常用元素操作
1.新建实例 xx_driver = os.path.abspath(r"路径") os.environ["webdriver.xx.driver"] = xx ...
- 【Fishing Master HDU - 6709 】【贪心】
题意分析 题意:题目给出n条鱼,以及捕一条鱼所用的时间k,并给出煮每一条鱼的时间,问抓完并煮完所有鱼的最短时间. 附题目链接 思路: 1.捕第一条鱼的时间是不可避免的,煮每条鱼的时间也是不可避免的,这 ...
- 通过类来实现多session 运行
#xilerihua import tensorflow as tf import numpy as np import os from PIL import Image import matplot ...
- Count the string[KMP]HDU3336
题库链接http://acm.hdu.edu.cn/showproblem.php?pid=3336 这道题是KMP的next数组的一个简单使用,首先要理解next数组的现实意义:next[i]表示模 ...