可重入锁ReentrantLock解析
说到可重入锁,先从AQS的ConditionObject说起,AQS的内部类ConditionObject是构建显示锁条件队列的基础。之前AQS的解析没有说这个内部类,这里和ReentrantLock一起说一下。
1、AQS的内部类ConditionObject
addConditionWaiter方法的代码:
这个方法的作用是将当前线程封装好放到条件队列。
1 private Node addConditionWaiter() {
2 Node t = lastWaiter;
3 // If lastWaiter is cancelled, clean out.
4 if (t != null && t.waitStatus != Node.CONDITION) {//如果等待队列的最后一个是取消状态,就把遍历整个队列把所有取消状态的节点清除
5 unlinkCancelledWaiters();
6 t = lastWaiter;
7 }
8 Node node = new Node(Thread.currentThread(), Node.CONDITION);//以当前线程新建一个节点
9 if (t == null)//根据条件将节点放入队列
10 firstWaiter = node;
11 else
12 t.nextWaiter = node;
13 lastWaiter = node;
14 return node;//返回当前线程的节点
15 }
doSignal方法的代码:
这个方法的作用就是唤醒当前线程。
1 private void doSignal(Node first) {
2 do {
3 if ( (firstWaiter = first.nextWaiter) == null)
4 lastWaiter = null;
5 first.nextWaiter = null;
6 } while (!transferForSignal(first) &&//调用了transferForSignal方法
7 (first = firstWaiter) != null);
8 }
transferForSignal方法的代码:
1 final boolean transferForSignal(Node node) {
2 /*
3 * If cannot change waitStatus, the node has been cancelled.
4 */
5 if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))//cas修改值,如果成功等待到了条件成立;如果失败节点的状态改为了CANCELLED,直接返回
6 return false;
7
8 /*
9 * Splice onto queue and try to set waitStatus of predecessor to
10 * indicate that thread is (probably) waiting. If cancelled or
11 * attempt to set waitStatus fails, wake up to resync (in which
12 * case the waitStatus can be transiently and harmlessly wrong).
13 */
14 Node p = enq(node);//节点入队 这里可能会有点晕,这个节点不是刚从队列里取出吗,怎么又入队了 其实是因为队列不是同一个,每个ConditionObject对象都有一个条件队列,队列里的节点等待的条件成立了会被添加到AQS的队列,这时节点(线程)才有资格等待获取资源
15 int ws = p.waitStatus;
16 if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))//唤醒线程
17 LockSupport.unpark(node.thread);
18 return true;
19 }
doSignalAll方法的代码:
这个方法的作用就是唤醒所有节点(线程)。
1 private void doSignalAll(Node first) {
2 lastWaiter = firstWaiter = null;
3 do {
4 Node next = first.nextWaiter;
5 first.nextWaiter = null;
6 transferForSignal(first);
7 first = next;
8 } while (first != null);
9 }
unlinkCancelledWaiters方法的代码:
这个方法被上面说的addConditionWaiter方法调用,将队列的取消状态的节点(线程)清除。
1 private void unlinkCancelledWaiters() {
2 Node t = firstWaiter;
3 Node trail = null;
4 while (t != null) {
5 Node next = t.nextWaiter;
6 if (t.waitStatus != Node.CONDITION) {
7 t.nextWaiter = null;
8 if (trail == null)
9 firstWaiter = next;
10 else
11 trail.nextWaiter = next;
12 if (next == null)
13 lastWaiter = trail;
14 }
15 else
16 trail = t;
17 t = next;
18 }
19 }
doSignal方法的代码:
1 public final void signal() {//唤醒一个节点(线程)
2 if (!isHeldExclusively())
3 throw new IllegalMonitorStateException();
4 Node first = firstWaiter;
5 if (first != null)
6 doSignal(first);
7 }
doSignalAll方法的代码:
1 public final void signalAll() {//唤醒所有节点
2 if (!isHeldExclusively())
3 throw new IllegalMonitorStateException();
4 Node first = firstWaiter;
5 if (first != null)
6 doSignalAll(first);
7 }
awaitUninterruptibly方法的代码:
这个方法的作用就是将节点(线程)放入条件队列。
1 public final void awaitUninterruptibly() {
2 Node node = addConditionWaiter();//放入条件队列
3 int savedState = fullyRelease(node);//释放资源
4 boolean interrupted = false;
5 while (!isOnSyncQueue(node)) {//当前节点是否在同步队列(在条件队列被唤醒后进入)
6 LockSupport.park(this);//如果不在,等待
7 if (Thread.interrupted())//检查是被中断还是被唤醒
8 interrupted = true;//如果是被中断设置interrupted,下面会设置中断标志
9 }
10 if (acquireQueued(node, savedState) || interrupted)//尝试获取资源 设置中断标志
11 selfInterrupt();
12 }
await方法的代码:
await方法和awaitUninterruptibly方法很相似,不同的地方是中断的处理。await方法的节点()线程如果是中断结束等待的,会根据当前中断的处理模式判断是抛出异常还是保留中断标志,不会再尝试获取资源。
1 public final void await() throws InterruptedException {
2 if (Thread.interrupted())
3 throw new InterruptedException();
4 Node node = addConditionWaiter();
5 int savedState = fullyRelease(node);
6 int interruptMode = 0;
7 while (!isOnSyncQueue(node)) {
8 LockSupport.park(this);
9 if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)//如果是中断,break
10 break;
11 }
12 if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
13 interruptMode = REINTERRUPT;
14 if (node.nextWaiter != null) // clean up if cancelled
15 unlinkCancelledWaiters();
16 if (interruptMode != 0)
17 reportInterruptAfterWait(interruptMode);//抛出异常或保留中断标志
18 }
ConditionObject的代码先说到这,下面说说ReentrantLock的代码。
2、ReentrantLock
ReentrantLock是独占模式的AQS,ReentrantLock有三个内部类,一个是抽象内部类。三个内部类为Sync、NonfairSync和FairSync。后面两个继承自前面一个。下面先说说这三个内部类。
Sync:
nonfairTryAcquire方法:
该方法的作用就是非公平地获取资源。
1 final boolean nonfairTryAcquire(int acquires) {
2 final Thread current = Thread.currentThread();
3 int c = getState();
4 if (c == 0) {//如果没有线程占有资源
5 if (compareAndSetState(0, acquires)) {//如果cas获取资源成功
6 setExclusiveOwnerThread(current);//将当前线程设置为独占资源的线程
7 return true;
8 }
9 }
10 else if (current == getExclusiveOwnerThread()) {//如果当前线程是占有资源的线程
11 int nextc = c + acquires;//增加state ReentrantLock里acquires都传1 state为n说明当前线程重入的次数为n - 1
12 if (nextc < 0)//溢出
13 throw new Error("Maximum lock count exceeded");
14 setState(nextc);//设置state值
15 return true;
16 }
17 return false;
18 }
tryRelease方法:
该方法的作用就是释放资源(锁),如果资源值(state)为0,则说明调用unlock的次数和调用lock的次数一样,锁可以释放供其它线程获取。
1 protected final boolean tryRelease(int releases) {
2 int c = getState() - releases;
3 if (Thread.currentThread() != getExclusiveOwnerThread())//如果当前线程不是独占占有资源的线程
4 throw new IllegalMonitorStateException();//抛出异常
5 boolean free = false;
6 if (c == 0) {//如果c等于0 资源全部释放(也就是释放了锁)
7 free = true;
8 setExclusiveOwnerThread(null);
9 }
10 setState(c);//设置state的值
11 return free;//返回是否释放了锁
12 }
newCondition方法:
该方法的作用就是创建新的条件队列。
1 final ConditionObject newCondition() {
2 return new ConditionObject();
3 }
NonfairSync:
lock方法:
非公平获取锁,每个线程获取锁时都会先cas试一下,如果成功了就直接获取了锁,不用再从入队出队开始。
1 final void lock() {
2 if (compareAndSetState(0, 1))
3 setExclusiveOwnerThread(Thread.currentThread());
4 else
5 acquire(1);//如果不成功还要排队继续
6 }
tryAcquire方法:
1 protected final boolean tryAcquire(int acquires) {
2 return nonfairTryAcquire(acquires);
3 }
FairSync:
lock方法:
公平获取锁,直接调用acquire,从入队出队开始。
final void lock() {
acquire(1);
}
tryAcquire方法:
tryAcquire方法和nonfairTryAcquire方法类似,主要区别在于tryAcquire要求获取资源的节点(线程)必须是队首节点。
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {//如果没有前驱节点(线程)且cas设置state成功,当前节点(线程)独占占有锁
setExclusiveOwnerThread(current);
return true;
}
}
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 ReentrantLock() {
2 sync = new NonfairSync();
3 }
4
5 public ReentrantLock(boolean fair) {
6 sync = fair ? new FairSync() : new NonfairSync();
7 }
8
9 public void lock() {
10 sync.lock();
11 }
12
13 public void lockInterruptibly() throws InterruptedException {
14 sync.acquireInterruptibly(1);
15 }
16
17 public boolean tryLock() {
18 return sync.nonfairTryAcquire(1);
19 }
20
21 public Condition newCondition() {
22 return sync.newCondition();
23 }
会发现这些方法基本上都是直接调用上面或之前说的那些方法。
可重入锁ReentrantLock解析的更多相关文章
- synchronized关键字,Lock接口以及可重入锁ReentrantLock
多线程环境下,必须考虑线程同步的问题,这是因为多个线程同时访问变量或者资源时会有线程争用,比如A线程读取了一个变量,B线程也读取了这个变量,然后他们同时对这个变量做了修改,写回到内存中,由于是同时做修 ...
- Java 重入锁 ReentrantLock 原理分析
1.简介 可重入锁ReentrantLock自 JDK 1.5 被引入,功能上与synchronized关键字类似.所谓的可重入是指,线程可对同一把锁进行重复加锁,而不会被阻塞住,这样可避免死锁的产生 ...
- 轻松学习java可重入锁(ReentrantLock)的实现原理
转载自https://blog.csdn.net/yanyan19880509/article/details/52345422,(做了一些补充) 前言 相信学过java的人都知道 synchroni ...
- java 可重入锁ReentrantLock的介绍
一个小例子帮助理解(我们常用的synchronized也是可重入锁) 话说从前有一个村子,在这个村子中有一口水井,家家户户都需要到这口井里打水喝.由于井水有限,大家只能依次打水.为了实现家家有水喝,户 ...
- 轻松学习java可重入锁(ReentrantLock)的实现原理(转 图解)
前言 相信学过java的人都知道 synchronized 这个关键词,也知道它用于控制多线程对并发资源的安全访问,兴许,你还用过Lock相关的功能,但你可能从来没有想过java中的锁底层的机制是怎么 ...
- 17_重入锁ReentrantLock
[概述] 重入锁可以完全代替synchronized关键字. 与synchronized相比,重入锁ReentrantLock有着显示的操作过程,即开发人员必须手动指定何时加锁,何时释放锁,所以重入锁 ...
- Java 显示锁 之 重入锁 ReentrantLock(七)
ReentrantLock 重入锁简介 重入锁 ReentrantLock,顾名思义,就是支持同一个线程对资源的重复加锁.另外,该锁还支持获取锁时的公平与非公平性的选择. 重入锁 ReentrantL ...
- Java并发(九):重入锁 ReentrantLock
先做总结: 1.为什么要用ReentrantLock? (1)ReentrantLock与synchronized具有相同的功能和内存语义: (2)synchronized是重量级锁,性能不好.Ree ...
- Java并发包4--可重入锁ReentrantLock的实现原理
前言 ReentrantLock是JUC提供的可重入锁的实现,用法上几乎等同于Synchronized,但是ReentrantLock在功能的丰富性上要比Synchronized要强大. 一.Reen ...
随机推荐
- Redis 实战 —— 07. 复制、处理故障、事务及性能优化
复制简介 P61 关系型数据库通常会使用一个主服务器 (master) 向多个从服务器 (slave) 发送更新,并使用从服务器来处理所有读请求. Redis 也采用了同样的方法实现自己的复制特性,并 ...
- 小白都看得懂的Javadoc使用教程
Javadoc是什么 官方回答: Javadoc is a tool for generating API documentation in HTML format from doc comments ...
- Jmeter如何录制APP客户端脚本
简单五步教大家Jmeter录制APP客户端脚本: Step1 右键单击该测试计划,选择"添加"-"线程组",添加一个线程组. Step2 为了录制客户端的操作, ...
- 【链表】leetcode-1290-二进制链表转整数
leetcode-1290-二进制链表转整数 题目描述 给你一个单链表的引用结点 head.链表中每个结点的值不是 0 就是 1.已知此链表是一个整数数字的二进制表示形式. 请你返回该链表所表示数字的 ...
- 在Ubuntu安装Docker
1.查看Linux内核依赖 kernel version >= 3.8 查看代码: uname -a | awk '{split($3, arr, "-"); print a ...
- JavaScript 类型、原型与继承学习笔记
目录 一.概览 二.数据类型 1. JavaScript中的数据类型 2. 什么是基本类型(Primitive Data Type) 2.1 概念 2.2 七个基本类型 2.3 基本类型封装对象 3. ...
- Linux CGroup入门
Linux cgroup Linux CGroup全称Linux Control Group, 是Linux内核的一个功能,用来限制,控制与分离一个进程组群的资源(如CPU.内存.磁盘输入输出等).L ...
- swap交换2变量
#define swap(x,y) {(x)=(x)+(y); (y)=(x)-(y); (x)=(x)-(y);} void swap(int i, int offset){ int temp; t ...
- 飞塔5.4和5.6版本IPSec互备冗余测试
主电信.备联通:测试方法:修改诚盈的IPSec,将阶段一电信的对端地址改为错误的. 方法一: 通过静态路由的管理距离:电信设置为10:联通为15.经测试,可以实现自动切换,且电信恢复后 可以切换回电信 ...
- .Net Core 3.1浏览器后端服务(一) Web API项目搭建
一.前言 基于CefSharp开发的浏览器项目已有一段时间,考虑到后期数据维护需要Server端来管理,故开启新篇章搭建浏览器后端服务.该项目前期以梳理服务端知识为主,后期将配合CefSharp浏览器 ...