说到可重入锁,先从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解析的更多相关文章

  1. synchronized关键字,Lock接口以及可重入锁ReentrantLock

    多线程环境下,必须考虑线程同步的问题,这是因为多个线程同时访问变量或者资源时会有线程争用,比如A线程读取了一个变量,B线程也读取了这个变量,然后他们同时对这个变量做了修改,写回到内存中,由于是同时做修 ...

  2. Java 重入锁 ReentrantLock 原理分析

    1.简介 可重入锁ReentrantLock自 JDK 1.5 被引入,功能上与synchronized关键字类似.所谓的可重入是指,线程可对同一把锁进行重复加锁,而不会被阻塞住,这样可避免死锁的产生 ...

  3. 轻松学习java可重入锁(ReentrantLock)的实现原理

    转载自https://blog.csdn.net/yanyan19880509/article/details/52345422,(做了一些补充) 前言 相信学过java的人都知道 synchroni ...

  4. java 可重入锁ReentrantLock的介绍

    一个小例子帮助理解(我们常用的synchronized也是可重入锁) 话说从前有一个村子,在这个村子中有一口水井,家家户户都需要到这口井里打水喝.由于井水有限,大家只能依次打水.为了实现家家有水喝,户 ...

  5. 轻松学习java可重入锁(ReentrantLock)的实现原理(转 图解)

    前言 相信学过java的人都知道 synchronized 这个关键词,也知道它用于控制多线程对并发资源的安全访问,兴许,你还用过Lock相关的功能,但你可能从来没有想过java中的锁底层的机制是怎么 ...

  6. 17_重入锁ReentrantLock

    [概述] 重入锁可以完全代替synchronized关键字. 与synchronized相比,重入锁ReentrantLock有着显示的操作过程,即开发人员必须手动指定何时加锁,何时释放锁,所以重入锁 ...

  7. Java 显示锁 之 重入锁 ReentrantLock(七)

    ReentrantLock 重入锁简介 重入锁 ReentrantLock,顾名思义,就是支持同一个线程对资源的重复加锁.另外,该锁还支持获取锁时的公平与非公平性的选择. 重入锁 ReentrantL ...

  8. Java并发(九):重入锁 ReentrantLock

    先做总结: 1.为什么要用ReentrantLock? (1)ReentrantLock与synchronized具有相同的功能和内存语义: (2)synchronized是重量级锁,性能不好.Ree ...

  9. Java并发包4--可重入锁ReentrantLock的实现原理

    前言 ReentrantLock是JUC提供的可重入锁的实现,用法上几乎等同于Synchronized,但是ReentrantLock在功能的丰富性上要比Synchronized要强大. 一.Reen ...

随机推荐

  1. 微服务网关1-Spring Cloud Gateway简介

    一.网关基本概念 1.API网关介绍 ​ API 网关出现的原因是微服务架构的出现,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各 ...

  2. innodb引擎的4大特性

    一:插入缓冲 二:二次写 三:自适应哈希 四:预读 1.插入缓冲(insert buffer)插入缓冲(Insert Buffer/Change Buffer):提升插入性能,change buffe ...

  3. 处理K8S PVC删除后pod报错

    报错如下 Jun 19 17:15:18 node1 kubelet[1722]: E0619 17:15:18.381558 1722 desired_state_of_world_populato ...

  4. tornado大全(甩锅版)

    tornado简介 tornado是Python界中非常出名的一款Web框架,和Flask一样它也属于轻量级的Web框架. 但是从性能而言tornado由于其支持异步非阻塞的特性所以对于一些高并发的场 ...

  5. 将连续增长 N 次字符串所需的内存重分配次数从必定 N 次降低为最多 N 次 二进制安全

    SDS 与 C 字符串的区别 - Redis 设计与实现 http://redisbook.com/preview/sds/different_between_sds_and_c_string.htm ...

  6. __init__ raises an exception, then __del__ will still be called

    issue 808164: socket.close() doesn't play well with __del__ - Python tracker https://bugs.python.org ...

  7. websocket心跳重连 websocket-heartbeat-js

    初探和实现websocket心跳重连(npm: websocket-heartbeat-js) 心跳重连缘由 websocket是前后端交互的长连接,前后端也都可能因为一些情况导致连接失效并且相互之间 ...

  8. Webpack4.0各个击破(10)integration篇

    一. Integration 下文摘自webpack中文网: 首先我们要消除一个常见的误解,webpack是一个模块打包工具(module bundler),它不是一个任务执行工具,任务执行器是用来自 ...

  9. CF413C

    正文 题意: 给 n 个关卡,每个关卡得分为 ai,有 m 次机会可以选择一 个关卡通过后不得分,而将现有得分翻倍 你可以安排关卡的通过顺序和策略,求最大得分. 分析: 看到这道题首先想到的就是贪心, ...

  10. HDU1814和平委员会

    题目大意: 有n对的人,编号从1-2*n,m对的人之间互相不喜欢,每对人中必徐选1个人加入和平委员会,求字典序最小的解 -------------------------------- 2-SAT问题 ...