上篇文章分析了ReentrantLock的lock,tryLock,unlock方法,继续分析剩下的方法,首先开始lockInterruptibly,先看其API说明:
lockInterruptiblypublic void lockInterruptibly()
throws Acquires the lock unless the current thread is .

Acquires the lock if it is not held by another thread and returns immediately, setting the lock hold count to one.

If the current thread already holds this lock then the hold count is incremented by one and the method returns immediately.

If the lock is held by another thread then the current thread becomes disabled for thread scheduling purposes and lies dormant until one of two things happens:

  • The lock is acquired by the current thread; or
  • Some other thread the current thread.

文档说明,只要线程没有处于interrupted状态,就尝试去获取锁,直到获取到锁,或者被interrupted。下面看其源码实现:

public void lockInterruptibly() throws InterruptedException {

sync.acquireInterruptibly(1);

}

acquireInterruptibly方法在AbstractQueuedSynchronizer中:

/**

* Acquires in exclusive mode, aborting if interrupted. Implemented by first

* checking interrupt status, then invoking at least once

* {@link #tryAcquire}, returning on success. Otherwise the thread is

* queued, possibly repeatedly blocking and unblocking, invoking


* {@link #tryAcquire} until success or the thread is interrupted. This

* method can be used to implement method {@link Lock#lockInterruptibly}.



* @param arg

*            the acquire argument. This value is conveyed to

*            {@link #tryAcquire} but is otherwise uninterpreted and can

*            represent anything you like.

* @throws InterruptedException

*             if the current thread is interrupted

*/

public final void acquireInterruptibly(int arg) throws InterruptedException {

if (Thread.interrupted())

throw new InterruptedException();

if (!tryAcquire(arg))

doAcquireInterruptibly(arg);

}

首先检查interrupted状态,调用tryAcquire方法,失败则:

/**

* Acquires in exclusive interruptible mode.



* @param arg

*            the acquire argument

*/

private void doAcquireInterruptibly(int arg) throws InterruptedException {

final Node node = addWaiter(Node.EXCLUSIVE);

try {

for (;;) {

final Node p = node.predecessor();

if (p == head && tryAcquire(arg)) {

setHead(node);

p.next = null; // help GC

return;

}

if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())

break;

}

} catch (RuntimeException ex) {

cancelAcquire(node);

throw ex;

}

// Arrive here only if interrupted

cancelAcquire(node);

throw new InterruptedException();

}

这段代码比较眼熟,这也是个阻塞方法,阻塞至node前继是head且exclusively owned synchronizer则返回,如果p is blocked and interrupted,这时线程处于interrupted状态,所以中止.

最后看一下ReentrantLock的newCondition方法,看这个方法之前先看看Doug Lea的论文《The java.util.concurrent Synchronizer Framework》,上面描述如下:

A ConditionObject uses the same internal queue nodes as synchronizers, but maintains them on a separate condition queue.The signal operation is implemented as a queue transfer from the condition queue to the lock queue, without necessarily waking up the signalled
thread before it has re-acquired its lock.The basic await operation is:
create and add new node to condition queue;

release lock;

block until node is on lock queue;

re-acquire lock;
And the signal operation is:
transfer the first node from condition queue to lock queue;

Because these operations are performed only when the lock is held, they can use sequential linked queue operations (using a nextWaiter field in nodes) to maintain the condition queue.

The transfer operation simply unlinks the first node from the condition queue.queue, and then uses CLH insertion to attach it to the lock queue.

继续看代码:

public Condition newCondition() {

return sync.newCondition();

}

newCondition在Sync类中:

final ConditionObject newCondition() {

return new ConditionObject();

}

直接返回一个ConditionObject 对象,ConditionObject 类是AbstractQueuedSynchronizer.java文件中。


来看看其声明的变量:

private static final long serialVersionUID = 1173984872572414699L;

/** First node of condition queue. */

private transient Node firstWaiter;

/** Last node of condition queue. */

private transient Node lastWaiter;

声明两个节点,通过后面的方法知道,这是一个单向链表。继续看其await方法:

public final void await() throws InterruptedException {

if (Thread.interrupted())

throw new InterruptedException();

Node node = addConditionWaiter();

int savedState = fullyRelease(node);

int interruptMode = 0;

while (!isOnSyncQueue(node)) {

LockSupport.park(this);

if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)

break;

}

if (acquireQueued(node, savedState) && interruptMode != THROW_IE)

interruptMode = REINTERRUPT;

if (node.nextWaiter != null)
unlinkCancelledWaiters();

if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);

}

检查状态后会调用addConditionWaiter方法:

/**

* Adds a new waiter to wait queue.



* @return its new wait node

*/

private Node addConditionWaiter() {

Node t = lastWaiter;

// If lastWaiter is cancelled, clean out.

if (t != null && t.waitStatus != Node.CONDITION) {

unlinkCancelledWaiters();

t = lastWaiter;

}

Node node = new Node(Thread.currentThread(), Node.CONDITION);

if (t == null)

firstWaiter = node;

else
t.nextWaiter = node;

lastWaiter = node
;

return node;

}

继续看unlinkCancelledWaiters方法:

/**

Unlinks cancelled waiter nodes from condition queue. Called only

* while holding lock. This is called when cancellation occurred during

* condition wait, and upon insertion of a new waiter when lastWaiter is

* seen to have been cancelled
. This method is needed to avoid garbage

* retention in the absence of signals. So even though it may require a

* full traversal, it comes into play only when timeouts or

* cancellations occur in the absence of signals. It traverses all nodes

* rather than stopping at a particular target to unlink all pointers to

* garbage nodes without requiring many re-traversals during

* cancellation storms.

*/

private void unlinkCancelledWaiters() {

Node t = firstWaiter;

Node trail = null;

while (t != null) {

Node next = t.nextWaiter;

if (t.waitStatus != Node.CONDITION) {

t.nextWaiter = null; //next=null

if (trail == null)

firstWaiter = next;

else

trail.nextWaiter = next;//tail.nextWaiter=null

if (next == null)

lastWaiter = trail;

} else

trail = t;

t = next;

}

}
遍历链表,如果某结点状态不为Node.CONDITION,则移除当前结点及后面的全部结点

addConditionWaiter方法首先会检查lastWaiter是否已经cancelled,是则清除链中cancelled的结点,否则添加当前结点为lastWaiter。


继续看fullyRelease方法:

/**

* Invokes release with current state value; returns saved state. Cancels

* node and throws exception on failure.



* @param node

*            the condition node for this wait

* @return previous sync state

*/

final int fullyRelease(Node node) {

try {

int savedState = getState();

if (release(savedState))

return savedState;

} catch (RuntimeException ex) {
node.waitStatus = Node.CANCELLED;//如果发生异常则将当前结点为置于CANCELLED状态

throw ex;

}

// reach here if release fails

node.waitStatus = Node.CANCELLED;

throw new IllegalMonitorStateException();

}
调用release方法交出synchronized排它持有权并unblock sync queue下一个结点,最后返回savedState 值

继续看下面的方法:

/**

* Returns true if a node, always one that was initially placed on a

* condition queue, is now waiting to reacquire on sync queue.



* @param node

*            the node

* @return true if is reacquiring

*/

final boolean isOnSyncQueue(Node node) {

if (node.waitStatus == Node.CONDITION || node.prev == null)

return false; //node在condition queue中

if (node.next != null) // If has successor, it must be on queue

return true; //node有前继结点也有后续结点,那么它在sync queue上但node结点不是tail

/*

* node.prev can be non-null, but not yet on queue because the CAS to

* place it on queue can fail. So we have to traverse from tail to make

* sure it actually made it. It will always be near the tail in calls to

* this method, and unless the CAS failed (which is unlikely), it will

* be there, so we hardly ever traverse much.

*/

return findNodeFromTail(node);

}

/**

Returns true if node is on sync queue by searching backwards from tail.

* Called only when needed by isOnSyncQueue.



* @return true if present

*/

private boolean findNodeFromTail(Node node) {

Node t = tail;

for (;;) {

if (t == node)

return true;

if (t == null)

return false;

t = t.prev;//从tail结点开始,逆序遍历查找node结点,成功则返回true

}

}

await会在while循环中判断:

/**

* Checks for interrupt, returning THROW_IE if interrupted before

* signalled, REINTERRUPT if after signalled, or 0 if not interrupted.

*/

private int checkInterruptWhileWaiting(Node node) {

return (Thread.interrupted()) ? ((transferAfterCancelledWait(node)) ? THROW_IE : REINTERRUPT) : 0;

}

/**

* Transfers node, if necessary, to sync queue after a cancelled wait.

* Returns true if thread was cancelled before being signalled.



* @param current

*            the waiting thread

* @param node

*            its node

* @return true if cancelled before the node was signalled.

*/

final boolean transferAfterCancelledWait(Node node) {

if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {

enq(node);//如果node状态为CONDITION,插入了sync queue尾部

return true;

}

/*

* If we lost out to a signal(), then we can't proceed until it finishes

* its enq(). Cancelling during an incomplete transfer is both rare and

* transient, so just spin.

*/

while (!isOnSyncQueue(node))//如果node不在sync queue中,那么让其他线程执行

Thread.yield();

return false;

}
如果当前node状态为Node.CONDITION,则会把该node添加到sync queue;如果当前线程interrupted,返回THROW_IE 

现在回顾一下await方法所进行的操作:
1.首先调用addConditionWaiter方法将当前线程以Node.CONDITION模式添加到condition queue中,如果队列lastWaiter处于cancelled状态,遍历队列移除cancelled状态的结点

2.调用fullyRelease方法交出synchronizer排它持有权并unpark sync queue的head后续结点,并返回之前的state值

3.循环判断当前node是否在sync queue上,不在则park当前线程,并检测当前线程是否处于interrupted状态,是则中断循环

4.调用acquireQueued阻塞方法,如果当前线程在signalled之前interrupted,设置interruptMode

5.如果node nextWaiter
不为空,则移除condition queue上cancelled的结点。

6.最后检测interruptMode,抛出异常或interrupt自身

       继续看其signal方法:
/**

* Moves the longest-waiting thread, if one exists, from the wait queue

* for this condition to the wait queue for the owning lock.

*

* @throws IllegalMonitorStateException

*             if {@link #isHeldExclusively} returns {@code false}

*/

public final void signal() {

if (!isHeldExclusively())

throw new IllegalMonitorStateException();

Node first = firstWaiter;

if (first != null)

doSignal(first);//取出condition queue第一个结点

}       

检测当前线程是否exclusively owned synchronizer,调用doSignal方法:
/**

* Removes and transfers nodes until hit non-cancelled one or null.

* Split out from signal in part to encourage compilers to inline the

* case of no waiters.



* @param first

*            (non-null) the first node on condition queue

*/

private void doSignal(Node first) {

do {

if ((firstWaiter = first.nextWaiter) == null)

lastWaiter = null;

first.nextWaiter = null;

} while (!transferForSignal(first) && (first = firstWaiter) != null); 

//注意这个循环条件,每次都将firstWaiter赋给first,这样保证操作的始终是队列中第一个结点即等待时间最长的线程
}

遍历condition queue,如果存在空结点,则null lastWaiter,
/**

* Transfers a node from a condition queue onto sync queue. Returns true if

* successful.



* @param node

*            the node

* @return true if successfully transferred (else the node was cancelled

*         before signal).

*/

final boolean transferForSignal(Node node) {

/*

* If cannot change waitStatus, the node has been cancelled.

*/

if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))

return false;//如果不是CONDITION状态,不会transfer node到sync queue上

/*

* Splice onto queue and try to set waitStatus of predecessor to

* indicate that thread is (probably) waiting. If cancelled or attempt

* to set waitStatus fails, wake up to resync (in which case the

* waitStatus can be transiently and harmlessly wrong).

*/

Node p = enq(node);

int c = p.waitStatus;

if (c > 0 || !compareAndSetWaitStatus(p, c, Node.SIGNAL))

LockSupport.unpark(node.thread);

return true;

}

如果当前node状态为Node.CONDITION,则将其插入到sync queue中并返回状态,如果状态值为cancelled,或者状态有变化,则unpark node所含的线程。

        现在回顾一下singal方法所进行的操作

1.检测

2.循环,反复调用transferForSignal操作condition queue中第一个结点,直到成功。


最后再看看signalAll方法的源码
/**

* Moves all threads from the wait queue for this condition to the wait

* queue for the owning lock.



* @throws IllegalMonitorStateException

*             if {@link #isHeldExclusively} returns {@code false}

*/

public final void signalAll() {

if (!isHeldExclusively())

throw new IllegalMonitorStateException();

Node first = firstWaiter;

if (first != null)

doSignalAll(first);

}

/**

* Removes and transfers all nodes.



* @param first

*            (non-null) the first node on condition queue

*/

private void doSignalAll(Node first) {

lastWaiter = firstWaiter = null;

do {

Node next = first.nextWaiter;

first.nextWaiter = null;

transferForSignal(first);

first = next;

} while (first != null);

}

遍历调用transferForSignal unpark结点所含的线程。
ReentrantLock的源码分析就到这里了,接下来想继续看看Semaphore,Latches等源码。

AbstractQueuedSynchronizer源码解析之ReentrantLock(二)的更多相关文章

  1. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  2. AbstractQueuedSynchronizer源码解析

    1.简介 AbstractQueuedSynchronizer队列同步器,用来实现锁或者其他同步组件的基础框架 AbstractQueuedSynchronizer使用int类型的volatile变量 ...

  3. [spring源码] 小白级别的源码解析ioc(二)

    之前一篇,整体描述了一下 Spring的整体概况和 jar包的介绍. 现在开始进入具体的源码解析,从本篇开始,先介绍spring的ioc容器.之前也看过一些介绍spring源码的, 有的是只讲整体的接 ...

  4. JDK8源码解析 -- HashMap(二)

    在上一篇JDK8源码解析 -- HashMap(一)的博客中关于HashMap的重要知识点已经讲了差不多了,还有一些内容我会在今天这篇博客中说说,同时我也会把一些我不懂的问题抛出来,希望看到我这篇博客 ...

  5. MyBatis源码解析(十二)——binding绑定模块之MapperRegisty

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6758456.html 1.回顾 之前解析了解析模块parsing,其实所谓的解析模块就是为 ...

  6. mybatis源码-解析配置文件(二)之解析的流程

    目录 1. 简介 2. 配置文件解析流程分析 2.1 调用 2.2 解析的目的 2.3 XML 解析流程 2.3.1 build(parser) 2.3.2 new XMLConfigBuilder( ...

  7. Mybaits 源码解析 (十二)----- Mybatis的事务如何被Spring管理?Mybatis和Spring事务中用的Connection是同一个吗?

    不知道一些同学有没有这种疑问,为什么Mybtis中要配置dataSource,Spring的事务中也要配置dataSource?那么Mybatis和Spring事务中用的Connection是同一个吗 ...

  8. bitcoin 源码解析 - 交易 Transaction(二) - 原理篇

    这篇文章我断断续续写了呃···· 应该快三个星期了? 所以前后的风格可能差别相当大.真是十分的怠惰啊··· 最近实在是不够努力.用python重写bitcoin的项目也卡在网络编程部分(这方面真是我的 ...

  9. AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

    1.  前言 Java中好多地方用到AbstractQueuedSynchronizer(PS:简称AQS),比如ReentrantLock.线程池,这部分在面试的时候也经常被问到,今天以Reentr ...

  10. Redis源码解析:21sentinel(二)定期发送消息、检测主观下线

    六:定时发送消息 哨兵每隔一段时间,会向其所监控的所有实例发送一些命令,用于获取这些实例的状态.这些命令包括:"PING"."INFO"和"PUBLI ...

随机推荐

  1. 神经网络之卷积篇:详解简单卷积网络示例(A simple convolution network example)

    详解简单卷积网络示例 假设有一张图片,想做图片分类或图片识别,把这张图片输入定义为\(x\),然后辨别图片中有没有猫,用0或1表示,这是一个分类问题,来构建适用于这项任务的卷积神经网络.针对这个示例, ...

  2. 淘宝订单信息获取接口API,淘宝打单发货接口

    从事电商软件开发的小伙伴,在日常开发任务中,经常会遇到一个需求,就是将淘宝店铺的订单,同步到自己的内部订单管理系统OMS中,进行淘宝打单发货操作.我介绍下如何将订单同步下来,供各位参考.(注意:所有电 ...

  3. 使用js闭包实现可取消的axios请求

    在平常开发中,经常会遇到重复请求的情况,也许是因为网络问题,也许是因为接口问题等等,传统做法是客户端采用防抖来限制用户发送接口的频率,一般出个loading转圈圈的形式, 但是很少使用取消请求的做法, ...

  4. OData – How It Work

    前言 OData 是很冷门的东西, 用的人少, 开发的人少, 文档自然也少的可怜. 如果真的想用它, 多少要对它机制有点了解. 这样遇到 bug, 想扩展的时候才不至于完全没有路. 主要参考: ODa ...

  5. ASP.NET Core – Swagger OpenAPI (Swashbuckle)

    前言 Swagger (OpenAPI) 是一套 Web API 文档规范. ASP.NET Core 有 2 个 Library 可用来实现 Swagger. Swashbuckle 和 NSwag ...

  6. 从零开始搭建一个LoRaWAN基站

    先说两句 SX1301/SX1302是semtech公司推出的基站端射频基带芯片,其与SX127x/SX126x的主要区别在于: 只是个基带芯片,使用时需要加射频前端(SX125x/SAW/...) ...

  7. Android Qcom USB Driver学习(五)

    前面的几篇都有涉及,所以本文学习一下pmic usb charger都相关的vote机制 OVP: Over Voltage Protection 过压保护 USB_IN: Input current ...

  8. window和Linux下安装nvidia的apex

    两种方法: 1.去github下下载apex,之后安装到你的python环境下,我的安装路径:E:\Anaconda\anaconda\envs\pytorch\Lib\site-packages 注 ...

  9. 墨天轮专访星环科技刘熙:“向量热”背后的冷思考,Hippo如何打造“先发”优势?

    导读: 深耕技术研发数十载,坚持自主可控发展路.星环科技一路砥砺前行.坚持创新为先,建设了全面的产品矩阵,并于2022年作为首个独立基础软件产品公司成功上市.星环科技在今年的向星力•未来技术大会上发布 ...

  10. C# 的运算符和作用域

    // C# 运算符 // 表达式 表达式有操作数(operand)和运算符(operator)构成: // 常见的运算符 + - * / 和 new // x ?? y 如果x为null, 则计算机过 ...