**FutureTask应用源码分析**(二)

2024-01-17 09:28

本文主要是介绍**FutureTask应用源码分析**(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.3.3 FutureTask的set&setException方法

任务执行完毕后,修改任务的状态以及封装任务的结果

// 没有异常的时候,正常返回结果
protected void set(V v) {
// 因为任务执行完毕,需要将任务的状态从NEW,修改为COMPLETING
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
// 将返回结果赋值给 outcome 属性
outcome = v;
// 将任务状态变为NORMAL,正常结束
UNSAFE.putOrderedInt(this, stateOffset, NORMAL);
// 一会再说……
finishCompletion();
}
}
// 任务执行期间出现了异常,这边要封装结果
protected void setException(Throwable t) {
// 因为任务执行完毕,需要将任务的状态从NEW,修改为COMPLETING
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
// 将异常信息封装到 outcome 属性
outcome = t;
// 将任务状态变为EXCEPTIONAL,异常结束
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL);
// 一会再说……
finishCompletion();
}
}

1.3.4 FutureTask的cancel方法

任务取消的一个方式
● 任务直接从NEW状态转换为CANCEL
● 任务从NEW状态变成INTERRUPTING,然后再转换为INTERRUPTED

// 取消任务操作
public boolean cancel(boolean mayInterruptIfRunning) {
// 查看任务的状态是否是NEW,如果NEW状态,就基于传入的参数mayInterruptIfRunning
// 决定任务是直接从NEW转换为CANCEL,还是从NEW转换为INTERRUPTING
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try {
// 如果mayInterruptIfRunning为true
// 就需要中断线程
if (mayInterruptIfRunning) {
try {
// 拿到任务线程
Thread t = runner;
if (t != null)
// 如果线程不为null,直接interrupt
t.interrupt();
} finally {
// 将任务状态设置为INTERRUPTED
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);}
}
} finally {
// 任务结束后的一些处理~~ 一会看~~
finishCompletion();
}
return true;
}

1.3.5 FutureTask的get方法

这个是线程获取FutureTask任务执行结果的方法

// 拿任务结果
public V get() throws InterruptedException, ExecutionException {
// 获取任务的状态
int s = state;
// 要么是NEW,任务还没执行完
// 要么COMPLETING,任务执行完了,结果还没封装好。
if (s <= COMPLETING)
// 让当前线程阻塞,等待结果
s = awaitDone(false, 0L);
// 最终想要获取结果,需要执行report方法
return report(s);
}
// 线程等待FutureTask结果的过程
private int awaitDone(boolean timed, long nanos) throws InterruptedException {
// 针对get方法传入了等待时长时,需要计算等到什么时间点
final long deadline = timed ? System.nanoTime() + nanos : 0L;
// 声明好需要的Node,queued:放到链表中了么?
WaitNode q = null;
boolean queued = false;
for (;;) {
// 查看线程是否中断,如果中断,从等待链表中移除,甩个异常
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
// 拿到状态
int s = state;
// 到这,说明任务结束了。
if (s > COMPLETING) {
if (q != null)
// 如果之前封装了WaitNode,现在要清空
q.thread = null;
return s;
}
// 如果任务状态是COMPLETING,这就不需要去阻塞线程,让步一下,等待一小会,结果就有了
else if (s == COMPLETING)
Thread.yield();
// 如果还没初始化WaitNode,初始化
else if (q == null)q = new WaitNode();
// 没放队列的话,直接放到waiters的前面
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
// 准备挂起线程,如果timed为true,挂起一段时间
else if (timed) {
// 计算出最多可以等待多久
nanos = deadline - System.nanoTime();
// 如果等待的时间没了
if (nanos <= 0L) {
// 移除当前的Node,返回任务状态
removeWaiter(q);
return state;
}
// 等一会
LockSupport.parkNanos(this, nanos);
}
else
// 死等
LockSupport.park(this);
}
}
// get的线程已经可以阻塞结束了,基于状态查看能否拿到返回结果
private V report(int s) throws ExecutionException {
// 拿到outcome 返回结果Object x = outcome;
// 如果任务状态是NORMAL,任务正常结束,返回结果
if (s == NORMAL)
return (V)x;
// 如果任务状态大于等于取消
if (s >= CANCELLED)
// 直接抛出异常
throw new CancellationException();
// 到这就是异常结束
throw new ExecutionException((Throwable)x);
}

1.3.6 FutureTask的finishCompletion方法

只要任务结束了,无论是正常返回,异常返回,还是任务被取消都会执行这个方法
而这个方法其实就是唤醒那些执行get方法等待任务结果的线程

// 任务结束后触发
private void finishCompletion() {
// 在任务结束后,需要唤醒
for (WaitNode q; (q = waiters) != null;) {
// 第一步直接以CAS的方式将WaitNode置为null
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
// 拿到了Node中的线程
Thread t = q.thread;// 如果线程不为null
if (t != null) {
// 第一步先置位null
q.thread = null;
// 直接唤醒这个线程
LockSupport.unpark(t);
}
// 拿到当前Node的next
WaitNode next = q.next;
// next为null,代表已经将全部节点唤醒了吗,跳出循环
if (next == null)
break;
// 将next置位null
q.next = null;
// q的引用指向next
q = next;
}
break;
}
}
// 任务结束后,可以基于这个扩展方法,记录一些信息
done();
// 任务执行完,把callable具体任务置位null
callable = null;
}

这篇关于**FutureTask应用源码分析**(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/615566

相关文章

Redis连接失败:客户端IP不在白名单中的问题分析与解决方案

《Redis连接失败:客户端IP不在白名单中的问题分析与解决方案》在现代分布式系统中,Redis作为一种高性能的内存数据库,被广泛应用于缓存、消息队列、会话存储等场景,然而,在实际使用过程中,我们可能... 目录一、问题背景二、错误分析1. 错误信息解读2. 根本原因三、解决方案1. 将客户端IP添加到Re

在Ubuntu上部署SpringBoot应用的操作步骤

《在Ubuntu上部署SpringBoot应用的操作步骤》随着云计算和容器化技术的普及,Linux服务器已成为部署Web应用程序的主流平台之一,Java作为一种跨平台的编程语言,具有广泛的应用场景,本... 目录一、部署准备二、安装 Java 环境1. 安装 JDK2. 验证 Java 安装三、安装 mys

Python中构建终端应用界面利器Blessed模块的使用

《Python中构建终端应用界面利器Blessed模块的使用》Blessed库作为一个轻量级且功能强大的解决方案,开始在开发者中赢得口碑,今天,我们就一起来探索一下它是如何让终端UI开发变得轻松而高... 目录一、安装与配置:简单、快速、无障碍二、基本功能:从彩色文本到动态交互1. 显示基本内容2. 创建链

Java汇编源码如何查看环境搭建

《Java汇编源码如何查看环境搭建》:本文主要介绍如何在IntelliJIDEA开发环境中搭建字节码和汇编环境,以便更好地进行代码调优和JVM学习,首先,介绍了如何配置IntelliJIDEA以方... 目录一、简介二、在IDEA开发环境中搭建汇编环境2.1 在IDEA中搭建字节码查看环境2.1.1 搭建步

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

Node.js 中 http 模块的深度剖析与实战应用小结

《Node.js中http模块的深度剖析与实战应用小结》本文详细介绍了Node.js中的http模块,从创建HTTP服务器、处理请求与响应,到获取请求参数,每个环节都通过代码示例进行解析,旨在帮... 目录Node.js 中 http 模块的深度剖析与实战应用一、引言二、创建 HTTP 服务器:基石搭建(一

锐捷和腾达哪个好? 两个品牌路由器对比分析

《锐捷和腾达哪个好?两个品牌路由器对比分析》在选择路由器时,Tenda和锐捷都是备受关注的品牌,各自有独特的产品特点和市场定位,选择哪个品牌的路由器更合适,实际上取决于你的具体需求和使用场景,我们从... 在选购路由器时,锐捷和腾达都是市场上备受关注的品牌,但它们的定位和特点却有所不同。锐捷更偏向企业级和专

java中VO PO DTO POJO BO DO对象的应用场景及使用方式

《java中VOPODTOPOJOBODO对象的应用场景及使用方式》文章介绍了Java开发中常用的几种对象类型及其应用场景,包括VO、PO、DTO、POJO、BO和DO等,并通过示例说明了它... 目录Java中VO PO DTO POJO BO DO对象的应用VO (View Object) - 视图对象

Go信号处理如何优雅地关闭你的应用

《Go信号处理如何优雅地关闭你的应用》Go中的优雅关闭机制使得在应用程序接收到终止信号时,能够进行平滑的资源清理,通过使用context来管理goroutine的生命周期,结合signal... 目录1. 什么是信号处理?2. 如何优雅地关闭 Go 应用?3. 代码实现3.1 基本的信号捕获和优雅关闭3.2

Spring中Bean有关NullPointerException异常的原因分析

《Spring中Bean有关NullPointerException异常的原因分析》在Spring中使用@Autowired注解注入的bean不能在静态上下文中访问,否则会导致NullPointerE... 目录Spring中Bean有关NullPointerException异常的原因问题描述解决方案总结