可重入锁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 ...
随机推荐
- uni-app开发经验分享十一: uniapp iOS云打包修改权限提示语
打包提交appstore如果用到了如下权限需要修改提示语,详细描述使用这个权限的原因,如不修改提示语appstore审核可能会被拒绝.Apple的原则是,如果一个app想要申请用户同意某个隐私信息访问 ...
- 给定HDFS中某一个目录,输出该目录下的所有文件的读写权限、大小、创建时间、路径等信息,如果该文件是目录,则递归输出该目录下所有文件相关信息。
1 import java.text.SimpleDateFormat; 2 import org.apache.hadoop.fs.*; 3 4 public class E_RecursiveRe ...
- 当中台遇上DDD,我们该如何设计微服务? - InfoQ https://www.infoq.cn/article/7QgXyp4Jh3-5Pk6LydWw
当中台遇上DDD,我们该如何设计微服务? - InfoQ https://www.infoq.cn/article/7QgXyp4Jh3-5Pk6LydWw
- V2版的接口在V3版里面都能找到对应接口 数据结构
开发文档 - 微信支付商户平台 https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/api.shtml 版本选择 关闭 V2版接口和V3版接口实际 ...
- 苹果 M1 芯片 OpenSSL 性能测试
Apple M1(MacBook Air 2020) type 16 bytes 64 bytes 256 bytes 1024 bytes 8192 bytes md2 0.00 0.00 0.00 ...
- webmvc 拦截器 允许跨域 跨域问题 sessionid不一样
package cn.com.yitong.ares.filter; import java.io.IOException; import javax.servlet.Filter;import ja ...
- JavaScript基础知识-基本概念
typeof操作符 typeof 操作符返回一个字符串,表示未经计算的操作数的类型. // 数值 typeof 37 === 'number'; typeof 3.14 === 'number'; t ...
- 在 WebAssembly 中实现回调的方式
本文将介绍在 C++ 中实现 js 回调的几种方式. 在使用 wasm 的过程中, 避免不了要从 C++ 回调 js 的函数来实现异步交互. 官网文档 https://emscripten.org/d ...
- Java——继承,重载,重写三剑客
About-继承 所有Java的类均是由java.lang.Object类继承而来的,所以Object是所有类的祖先类,而除了Object外,所有类必须有一个父类. 继承可以理解为一个对象从另一个对象 ...
- 网络编程(socket简介)
socket简介 Python 提供了两个基本的 socket 模块. 第一个是 Socket,它提供了标准的 BSD Sockets API. 第二个是 SocketServer, 它提供了服务器中 ...