JUC代码浅析[1]——同步器AQS
AQS(AbstractQueuedSynchronizer)是一个提供实现各种锁和同步器的基本框架,它实现了调度逻辑,留出具体的进入和释放规则给子类实现。JUC中基于AQS实现的有ReentrantLock,Semaphore,CountDownLatch, ReetrantRead
WriteLock,FutureTask等。代码非常复杂很难描述清楚每个细节,可能还有一些理解偏差的地方。总的来说底层实现的要点有Unsafe.compareAndSwapXXX(Object
o,long offset,int expected,int x);Unsafe.park() 挂起线程 ;Unsafe.unpark()唤醒被park的线程;双向列表存储线程的队列。
被park的线程有3种方式可以唤醒它,park返回时不会报告是被哪种方式唤醒的,可以用Thread.interrupted()检查,
l
unpark(Thread thread),这种方式唤醒时Thread.interrupted()返回的是false
l
thread.interrupt(),这种方式唤醒时Thread.interrupted()返回的是true
l
不知原因的返回
队列中存放着Node代表等待线程,它包含4中状态CANCELLED(1):当前节点已经退出了,SIGNAL(-1):后继者需要被唤醒,CONDITION(-2):节点在condition队列中等待,0;非负数代表节点不需要被唤醒了。节点有共享和互斥两种模式。
AQS提供给子类一个int state属性通过getState()和setState()暴露给子类,子类通过state和实现以下5个方法来实现不同的需求
l
tryAcquire 互斥模式下尝试获得许可(一般需要结合state的值)
l
tryRelease 互斥模式下尝试释放许可并设置state
l
tryAcquireShared 共享模式下尝试获得许可
l
tryReleaseShared 共享模式下尝试释放许可并设置state
l
isHeldExclusively 当前线程是否占有许可
外部获取许可总的来说就是尝试获取许可,不成功时进入列表队列等待再次尝试或挂起或被打断。具体实现看互斥不可中断模式的acquire方法,
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
首先尝试获取许可(也就是子类要实现的),不成功时当前线程添加到列表的最后并等待,必要时挂起或打断线程(注意何时挂起何时打断)。
其中addWaiter方法主要使用自旋锁和Unsafe.compareAndSwapXXX方法保证列表状态的一致.
acquireQueued方法不断的检查和移动队列,当当前线程到达位于队列头下一个的时候尝试获取
final boolean acquireQueued(final Node node, int arg) {
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
return interrupted;
}
if (shouldParkAfterFailedAcquire(p,
node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} catch (RuntimeException ex) {
cancelAcquire(node);
throw ex;
}
}
下面看如何检查和移动队列的
private static boolean shouldParkAfterFailedAcquire(Node
pred, Node node) {
int s = pred.waitStatus;
if (s < 0)
/*
* 前一个节点的等待状态小于0,则当前节点被park起来
*
*/
return true;
if (s > 0) {
/*
* 前面节点是退出状态,则调整指针往前查询跳过其他退出状态的节点,
* 直到找到不是退出状态的节点作为它的前一个节点
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
}
else
/*
* 标识当前节点需要被signal(以前一个节点状态为SIGNAL表示),以免期间发生 * 了请求操作需要继续自旋检查
*/
compareAndSetWaitStatus(pred,
0, Node.SIGNAL);
return false;
}
释放许可。互斥不可中断模式的release方法,
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
尝试释放成功时,唤醒列表队列的下一个节点(如果需要的话)
private void unparkSuccessor(Node node) {
/*
* 清除节点的SIGNAL状态 (接下来的唤醒操作之后,表示它的下一个节点不需要被唤
* 醒了)
*/
compareAndSetWaitStatus(node, Node.SIGNAL, 0);
/*
* 唤醒一个后继节点,
如果后一个节点不存在或者是退出状态则从尾部开始向前找到一个需要被唤醒的节点
*/
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
另外还有互斥可中断模式、共享不可中断模式、共享可中断模式。
可中断和不可中断模式的区别在于可中断模式接收到中断信号后立即中断,不可中断模式要等待节点到达列表第一个位置才中断(中断过程还要进行一些操作)。
共享模式尝试获取是执行tryAcquireShared方法并把节点标识为共享模式,其他的操作跟互斥模式有所不同但大同小异。具体的就不详细展开了。
分享到:
相关推荐
狂神说JUC代码
juc 的aqs介绍。
AQS 是 JUC 中的核心组件,它提供了一个框架,让开发者能够轻松地构建自定义的同步器。通过继承 AQS,可以实现不同的同步机制,如独占锁、共享锁等。 高效的等待队列: AQS 内部维护了一个等待队列,用于管理等待锁...
JUC代码收集,java高并发多线程学习
个人学习的时候对JUC做的笔记,包括所有代码,学习视频是尚硅谷周阳老师的JUC,需要有一定的基础才能看懂,这是个人笔记,不一定适用于所有人,谢谢!
Java 并发编程在现代软件开发中占据重要地位,尤其是在多核处理器的时代。JUC(java.util.concurrent)库是 Java ...4. 同步器(Synchronizers):JUC 中的同步器主要通过 AQS(AbstractQueuedSynchronizer)提供支持。
Java 多线程与并发(10_26)-JUC锁_ 锁核心类AQS详解
JUC代码演示 Java多线程并发 源于B站https://www.bilibili.com/video/BV1Kw411Z7dF/?p=32&spm_id_from=333.1007.top_right_bar_window_history.content.click课程自我项目记录。 适合有一定基础的朋友。
为了学习JUC,AQS是基础中的基础,所以我们首先深入了解下AQS。 一、锁的介绍 为了了解AQS的源码,我们需要先大概下锁中的一些功能 1.1 乐观锁/悲观锁 乐观锁与悲观锁是一种广义上的概念,体现了看待线程同步的不同...
JUC是什么 线程 进程 / 线程 线程状态 wait / sleep 并发 / 并行 Lock 使用Lock锁 可重入锁 公平锁 / 非公平锁 Synchronized / Lock 线程通讯 wait()、notify()和notifyAll() 虚假唤醒 Condition 定制化通信 多线程...
本资源描述了Java并发常见的问题AQS的加锁解锁的过程
juc学习代码。。。。
juc并发编程脑图以及相关示例代码
【尚硅谷】大厂必备技术之JUC并发编程视频 配套资料,自己根据视频整理 pdf 课件,和代码 视频地址:...
ReentrantLock Lock 加锁过程源码分析图,AQS 源码分析
用于定义类似于线程的自定义子系统,包括线程池,异步 IO 和轻量级任务框架;包含下载地址
详细阐述了ReentrantLock通过AQS获取锁到释放锁的过程,附有关键方法的源码及注释
尚硅谷_JUC线程高级_源码、课件 ·1. 尚硅谷_JUC线程高级_volatile 关键字与内存可见性 ·2. 尚硅谷_JUC线程高级_原子变量与 CAS 算法 ·3. 尚硅谷_JUC线程高级_模拟 CAS 算法 ·4. 尚硅谷_JUC线程高级_同步容器类...
从JUC中的AQS引入,讲解Java volatile与AQS锁内存可见性
多线程基础、JUC锁、同步容器、线程池