《java.util.concurrent 包源码阅读》28 Phaser 第二部分
这一部分来分析Phaser关于线程等待的实现。所谓线程等待Phaser的当前phase结束并转到下一个phase的过程。Phaser提供了三个方法:
// 不可中断,没有超时的版本
public int awaitAdvance(int phase); // 可以中断,没有超时的版本
public int awaitAdvanceInterruptibly(int phase); // 可以中断,带有超时的版本
public int awaitAdvanceInterruptibly(int phase, long timeout, TimeUnit unit);
这三个版本的方法的实现大体类似,区别在于第二个版本多了中断异常,第三个版本多了中断异常和超时异常。
public int awaitAdvance(int phase) {
// 获取当前state
final Phaser root = this.root;
long s = (root == this) ? state : reconcileState();
int p = (int)(s >>> PHASE_SHIFT);
// 检查给定的phase是否和当前的phase一直
if (phase < 0)
return phase;
if (p == phase)
return root.internalAwaitAdvance(phase, null);
return p;
}
// 多了一个对于中断的检查然后抛出中断异常
public int awaitAdvanceInterruptibly(int phase)
throws InterruptedException {
final Phaser root = this.root;
long s = (root == this) ? state : reconcileState();
int p = (int)(s >>> PHASE_SHIFT);
if (phase < 0)
return phase;
if (p == phase) {
// 使用QNode实现中断和超时,这里不带超时
QNode node = new QNode(this, phase, true, false, 0L);
p = root.internalAwaitAdvance(phase, node);
// 对于中断的情况,抛出中断异常
if (node.wasInterrupted)
throw new InterruptedException();
}
return p;
}
// 多了中断异常和超时异常
public int awaitAdvanceInterruptibly(int phase,
long timeout, TimeUnit unit)
throws InterruptedException, TimeoutException {
long nanos = unit.toNanos(timeout);
final Phaser root = this.root;
long s = (root == this) ? state : reconcileState();
int p = (int)(s >>> PHASE_SHIFT);
if (phase < 0)
return phase;
if (p == phase) {
QNode node = new QNode(this, phase, true, true, nanos);
p = root.internalAwaitAdvance(phase, node);
// 中断异常
if (node.wasInterrupted)
throw new InterruptedException();
// 没有进入下一个phase,抛出超时异常
else if (p == phase)
throw new TimeoutException();
}
return p;
}
上述三个方法都是调用了internalAwaitAdvance方法来实现等待,因此来看internalAwaitAdvance方法:
private int internalAwaitAdvance(int phase, QNode node) {
// 释放上一个phase的资源
releaseWaiters(phase-1);
// node是否被加入到队列中
boolean queued = false;
// 记录前一个Unarrived,用来增加spin值
int lastUnarrived = 0;
int spins = SPINS_PER_ARRIVAL;
long s;
int p;
// 循环操作直到phase值发生了变化
while ((p = (int)((s = state) >>> PHASE_SHIFT)) == phase) {
// 不可中断的模式,使用自旋等待
if (node == null) {
int unarrived = (int)s & UNARRIVED_MASK;
if (unarrived != lastUnarrived &&
(lastUnarrived = unarrived) < NCPU)
spins += SPINS_PER_ARRIVAL;
boolean interrupted = Thread.interrupted();
// 发生了中断时,使用一个node来记录这个中断
if (interrupted || --spins < 0) {
node = new QNode(this, phase, false, false, 0L);
node.wasInterrupted = interrupted;
}
}
// 当前线程的node可以结束等待了,后面会分析isReleasible方法
else if (node.isReleasable())
break;
// 把node加入到队列中
else if (!queued) {
// 根据phase值不同,使用不同的队列
AtomicReference<QNode> head = (phase & 1) == 0 ? evenQ : oddQ;
QNode q = node.next = head.get();
// 检查队列的phase是否和要求的phase一致并且Phaser的phase没有发生变化
// 符合这两个条件才把node添加到队列中去
if ((q == null || q.phase == phase) &&
(int)(state >>> PHASE_SHIFT) == phase)
queued = head.compareAndSet(q, node);
}
// node加入队列后直接等待
else {
try {
// 对于普通线程来说,这个方法作用就是循环直到isReleasable返回true
// 或者block方法返回true
ForkJoinPool.managedBlock(node);
} catch (InterruptedException ie) {
node.wasInterrupted = true;
}
}
}
// 对于进入队列的node,重置一些属性
if (node != null) {
// 释放thread,不要再使用unpark
if (node.thread != null)
node.thread = null;
// 对于不可中断模式下发生的中断,清除中断状态
if (node.wasInterrupted && !node.interruptible)
Thread.currentThread().interrupt();
// phase依旧没有变化表明同步过程被终止了
if (p == phase && (p = (int)(state >>> PHASE_SHIFT)) == phase)
return abortWait(phase);
}
// 通知所有的等待线程
releaseWaiters(phase);
return p;
}
下面来看QNode,它实现了ManagedBlocker接口(见ForkJoinPool),ManagedBlocker包含两个方法:isReleasable和block。
isReleasable表示等待可以结束了,下面是QNode实现的isReleasable:
public boolean isReleasable() {
// 没了等待线程,通常会在外部使用"node.thread = null"来释放等待线程,这时可以结束等待
if (thread == null)
return true;
// phase发生变化,可以结束等待
if (phaser.getPhase() != phase) {
thread = null;
return true;
}
// 可中断的情况下发生线程中断,可以结束等待
if (Thread.interrupted())
wasInterrupted = true;
if (wasInterrupted && interruptible) {
thread = null;
return true;
}
// 设置超时的情况下,发生超时,可以结束等待
if (timed) {
if (nanos > 0L) {
long now = System.nanoTime();
nanos -= now - lastTime;
lastTime = now;
}
if (nanos <= 0L) {
thread = null;
return true;
}
}
return false;
}
最后来看QNode实现的block方法,核心思想是用LockSupport来实现线程等待:
public boolean block() {
if (isReleasable())
return true;
// 没有设置超时的情况
else if (!timed)
LockSupport.park(this);
// 设置超时的情况
else if (nanos > 0)
LockSupport.parkNanos(this, nanos);
return isReleasable();
}
最后来看releaseWaiters方法,看看怎么释放node队列:
private void releaseWaiters(int phase) {
QNode q;
Thread t;
AtomicReference<QNode> head = (phase & 1) == 0 ? evenQ : oddQ;
// 如果phase已经发生了变化,才能释放
while ((q = head.get()) != null &&
q.phase != (int)(root.state >>> PHASE_SHIFT)) {
// 释放节点并转到下一个节点
if (head.compareAndSet(q, q.next) &&
(t = q.thread) != null) {
// 释放线程
q.thread = null;
// 通知线程结束等待
LockSupport.unpark(t);
}
}
}
到这里就把Phaser分析完了。
《java.util.concurrent 包源码阅读》28 Phaser 第二部分的更多相关文章
- 《java.util.concurrent 包源码阅读》 结束语
<java.util.concurrent 包源码阅读>系列文章已经全部写完了.开始的几篇文章是根据自己的读书笔记整理出来的(当时只阅读了部分的源代码),后面的大部分都是一边读源代码,一边 ...
- 《java.util.concurrent 包源码阅读》13 线程池系列之ThreadPoolExecutor 第三部分
这一部分来说说线程池如何进行状态控制,即线程池的开启和关闭. 先来说说线程池的开启,这部分来看ThreadPoolExecutor构造方法: public ThreadPoolExecutor(int ...
- 《java.util.concurrent 包源码阅读》02 关于java.util.concurrent.atomic包
Aomic数据类型有四种类型:AomicBoolean, AomicInteger, AomicLong, 和AomicReferrence(针对Object的)以及它们的数组类型, 还有一个特殊的A ...
- 《java.util.concurrent 包源码阅读》04 ConcurrentMap
Java集合框架中的Map类型的数据结构是非线程安全,在多线程环境中使用时需要手动进行线程同步.因此在java.util.concurrent包中提供了一个线程安全版本的Map类型数据结构:Concu ...
- 《java.util.concurrent 包源码阅读》17 信号量 Semaphore
学过操作系统的朋友都知道信号量,在java.util.concurrent包中也有一个关于信号量的实现:Semaphore. 从代码实现的角度来说,信号量与锁很类似,可以看成是一个有限的共享锁,即只能 ...
- 《java.util.concurrent 包源码阅读》06 ArrayBlockingQueue
对于BlockingQueue的具体实现,主要关注的有两点:线程安全的实现和阻塞操作的实现.所以分析ArrayBlockingQueue也是基于这两点. 对于线程安全来说,所有的添加元素的方法和拿走元 ...
- 《java.util.concurrent 包源码阅读》09 线程池系列之介绍篇
concurrent包中Executor接口的主要类的关系图如下: Executor接口非常单一,就是执行一个Runnable的命令. public interface Executor { void ...
- 《java.util.concurrent 包源码阅读》05 BlockingQueue
想必大家都很熟悉生产者-消费者队列,生产者负责添加元素到队列,如果队列已满则会进入阻塞状态直到有消费者拿走元素.相反,消费者负责从队列中拿走元素,如果队列为空则会进入阻塞状态直到有生产者添加元素到队列 ...
- 《java.util.concurrent 包源码阅读》10 线程池系列之AbstractExecutorService
AbstractExecutorService对ExecutorService的执行任务类型的方法提供了一个默认实现.这些方法包括submit,invokeAny和InvokeAll. 注意的是来自E ...
随机推荐
- Angular4 后台管理系统搭建(10) - 做一个通用的可跨域上传文件的组件
写的很慢,不知不觉这是第十篇了.但是我其他事情太多,只能抽空写下.现在angular4或angular2流行的上传方式是ng2-file-upload.它的功能很强大.但是我没有配置成可以跨域上传的. ...
- aspnet中通过多条件筛选来显示数据的实现
UI图: 功能实现: 1.勾选住哪个选项之后,就加入了筛选.支持姓名的模糊查询. 2.对筛选出来的数据可以直接修改,并更新回数据库. 说明:显示的数据来自T_User表.数据显示控件使用的是 List ...
- 张高兴的 Windows 10 IoT 开发笔记:DHT11 温湿度传感器
GitHub : https://github.com/ZhangGaoxing/windows-iot-demo/tree/master/DHT11Demo
- 带你领会 线性代数 微积分的本质 3blue1brown 动画效果帅出天际
前段时间在 哔哩哔哩 上偶然发现了 3blue1brown 精美的动画,配上生动的讲解,非常适合帮助建立数学的形象思维 其中两大系列,非常值得反复观看: 线性代数的本质(Essence of line ...
- Cosmos OpenSSD--greedy_ftl1.2.0(二)
FTL的整个流程如下: 下面先来看写的流程: 写的代码如下: if((hostCmd.reqInfo.Cmd == IDE_COMMAND_WRITE_DMA) || (hostCmd.reqInfo ...
- angular1与swiper
angular1路由切换过程中swiper不能使用. 问题1:在刚开始使用angular1的路由时,好多人会将swiper的初始化写在模板的父控制器上,这样会造成一个问题,swiper的初始化只在页面 ...
- phalcon——访问控制列表ACL
一个完整的使用实例(将acl封装成一个插件使用): use Phalcon\Acl; use Phalcon\Acl\Role; use Phalcon\Acl\Resource; use Phalc ...
- 笔记-windbg及时调试
当程序在测试或者老化的时候很有用,只要程序有异常抛出,就能启用windbg调试,这样就能及时的保存现场. 程序崩溃时,windows系统会调用系统默认调试器,其设置在注册表 HKEY_LOCAL_MA ...
- Java基础——输入输出
在程序最开始 import java.util.*; 因为Scanner类定义在java.util包中. 读取输入: 先构造一个Scanner对象,并与"标准输入流"System ...
- 给资源文件添加指纹(Gulp版)
至于为什么要费尽心思地给文件添加指纹,请参看前端静态资源缓存控制策略.这次要达到的小目标就是生成的资源文件能够被客户端缓存,而在文件内容变化后,能够请求到最新的文件. 需要用到的 gulp 插件是 g ...