说到可重入锁,先从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. 求得二叉搜索树的第k小的元素

    求得二叉搜索树的第k小的元素 给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素. 须知:二叉搜索树,又叫二叉排序树,二叉查找树.特点是:左子树的所有元素都小于等 ...

  2. 关于java并发场景下,HttpServletRequst中session丢失问题

    使用场景: 在list数据进来之后使用安全数组    Lists.newCopyOnWriteArrayList() 进行了   parallelStream  并行处理,在接口中进行了登录者信息接口 ...

  3. .NetCore 在不同位置添加过滤器

    前言 以ParaModelValidateAttribute(参数校验)和ErrorCatch(错误捕捉)为例. 在方法上添加(局部) 这种方式比较灵活 [ParaModelValidate] [Er ...

  4. Kubernetes 存储简介

    存储分类结构图 半持久化存储 1.EmptyDir EmptyDir是一个空目录,生命周期和所属的 Pod 是完全一致的,EmptyDir的用处是,可以在同一 Pod 内的不同容器之间共享工作过程中产 ...

  5. 代码托管从业者 Git 指南

    本文作者:李博文 - CODING 后端开发工程师 前言 六七年前,我机缘巧合进入了代码托管行业,做过基于 Git 支持 SVN 客户端接入.Git 代码托管平台分布式.Git 代码托管读写分离.Gi ...

  6. JVM(三)从JVM源码角度看类加载器层级关系和双亲委派

    类加载器我们都知道有如下的继承结构,这个关系只是逻辑上的父子关系. 我们一直听说引导类加载器没有实体,为什么没有实体呢? 因为引导类加载器只是一段C++代码并不是什么实体类,所谓的引导类加载器就是那一 ...

  7. Spring之 IOC&依赖注入

    0x01.Spring 1什么是Spring ​ Spring 是一个开源框架,是为了解决企业应用程序开发复杂性而创建的(解耦). ​ 框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组 ...

  8. Optimistic concurrency control 死锁 悲观锁 乐观锁 自旋锁

    Optimistic concurrency control https://en.wikipedia.org/wiki/Optimistic_concurrency_control Optimist ...

  9. Scala:case class

    Scala:case class 1.Scala中class.object.case class.case object区别 1.1 class 和 object 关系 1.2 case class ...

  10. 设计模式c++(5)——工厂模式

    工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个.工厂方法让类把实例化推迟到子类. 总的来说,就是工厂方法模式封装了对象的创建,通过让子类决定该创建的对象是什么,来达到将对象创建 ...