详细介绍FutureTask类
一、详细介绍FutureTask类
FutureTask 未来将要执行的任务对象,继承 Runnable、Future 接口,用于包装 Callable 对象,实现任务的提交
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> task = new FutureTask<>(new Callable<String>() {
@Override
public String call() throws Exception {
return "Hello World";
}
});
new Thread(task).start(); //启动线程
String msg = task.get(); //获取返回任务数据
System.out.println(msg);
}
构造方法
public FutureTask(Callable<V> callable){
this.callable = callable; // 属性注入
this.state = NEW; // 任务状态设置为 new
}
public FutureTask(Runnable runnable, V result) {
// 适配器模式
this.callable = Executors.callable(runnable, result);
this.state = NEW;
}
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null) throw new NullPointerException();
// 使用装饰者模式将 runnable 转换成 callable 接口,外部线程通过 get 获取
// 当前任务执行结果时,结果可能为 null 也可能为传进来的值,【传进来什么返回什么】
return new RunnableAdapter<T>(task, result);
}
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
// 构造方法
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
// 实则调用 Runnable#run 方法
task.run();
// 返回值为构造 FutureTask 对象时传入的返回值或者是 null
return result;
}
}
成员属性
任务状态
// 表示当前task状态
private volatile int state;
// 当前任务尚未执行
private static final int NEW = 0;
// 当前任务正在结束,尚未完全结束,一种临界状态
private static final int COMPLETING = 1;
// 当前任务正常结束
private static final int NORMAL = 2;
// 当前任务执行过程中发生了异常,内部封装的 callable.run() 向上抛出异常了
private static final int EXCEPTIONAL = 3;
// 当前任务被取消
private static final int CANCELLED = 4;
// 当前任务中断中
private static final int INTERRUPTING = 5;
// 当前任务已中断
private static final int INTERRUPTED = 6;
任务对象
// Runnable 使用装饰者模式伪装成 Callable
private Callable<V> callable;
存储任务执行的结果,这是 run 方法返回值是 void 也可以获取到执行结果的原因
// 正常情况下:任务正常执行结束,outcome 保存执行结果,callable 返回值
// 非正常情况:callable 向上抛出异常,outcome 保存异常
private Object outcome;
执行当前任务的线程对象
// 当前任务被线程执行期间,保存当前执行任务的线程对象引用
private volatile Thread runner;
线程阻塞队列的头节点
// 会有很多线程去 get 当前任务的结果,这里使用了一种数据结构头插头取(类似栈)的一个队列来保存所有的 get 线程
private volatile WaitNode waiters;
内部类
static final class WaitNode {
// 单向链表
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
成员方法
FutureTask 类的成员方法:
FutureTask#run:任务执行入口
public void run() {
//条件一:成立说明当前 task 已经被执行过了或者被 cancel 了,非 NEW 状态的任务,线程就不需要处理了
//条件二:线程是 NEW 状态,尝试设置当前任务对象的线程是当前线程,设置失败说明其他线程抢占了该任务,直接返回
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
return;
try {
// 执行到这里,当前 task 一定是 NEW 状态,而且【当前线程也抢占 task 成功】
Callable<V> c = callable;
// 判断任务是否为空,防止空指针异常;判断 state 状态,防止外部线程在此期间 cancel 掉当前任务
// 【因为 task 的执行者已经设置为当前线程,所以这里是线程安全的】
if (c != null && state == NEW) {
V result;
// true 表示 callable.run 代码块执行成功 未抛出异常
// false 表示 callable.run 代码块执行失败 抛出异常
boolean ran;
try {
// 【调用自定义的方法,执行结果赋值给 result】
result = c.call();
// 没有出现异常
ran = true;
} catch (Throwable ex) {
// 出现异常,返回值置空,ran 置为 false
result = null;
ran = false;
// 设置返回的异常
setException(ex);
}
// 代码块执行正常
if (ran)
// 设置返回的结果
set(result);
}
} finally {
// 任务执行完成,取消线程的引用,help GC
runner = null;
int s = state;
// 判断任务是不是被中断
if (s >= INTERRUPTING)
// 执行中断处理方法
handlePossibleCancellationInterrupt(s);
}
}
FutureTask#set:设置正常返回值,首先将任务状态设置为 COMPLETING 状态代表完成中,逻辑执行完设置为 NORMAL 状态代表任务正常执行完成,最后唤醒 get() 阻塞线程
protected void set(V v) {
// CAS 方式设置当前任务状态为完成中,设置失败说明其他线程取消了该任务
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
// 【将结果赋值给 outcome】
outcome = v;
// 将当前任务状态修改为 NORMAL 正常结束状态。
UNSAFE.putOrderedInt(this, stateOffset, NORMAL);
finishCompletion();
}
}
FutureTask#setException:设置异常返回值
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
// 赋值给返回结果,用来向上层抛出来的异常
outcome = t;
// 将当前任务的状态 修改为 EXCEPTIONAL
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL);
finishCompletion();
}
}
FutureTask#finishCompletion:唤醒 get() 阻塞线程
private void finishCompletion() {
// 遍历所有的等待的节点,q 指向头节点
for (WaitNode q; (q = waiters) != null;) {
// 使用cas设置 waiters 为 null,防止外部线程使用cancel取消当前任务,触发finishCompletion方法重复执行
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
// 自旋
for (;;) {
// 获取当前 WaitNode 节点封装的 thread
Thread t = q.thread;
// 当前线程不为 null,唤醒当前 get() 等待获取数据的线程
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
// 获取当前节点的下一个节点
WaitNode next = q.next;
// 当前节点是最后一个节点了
if (next == null)
break;
// 断开链表
q.next = null; // help gc
q = next;
}
break;
}
}
done();
callable = null; // help GC
}
FutureTask#handlePossibleCancellationInterrupt:任务中断处理
private void handlePossibleCancellationInterrupt(int s) {
if (s == INTERRUPTING)
// 中断状态中
while (state == INTERRUPTING)
// 等待中断完成
Thread.yield();
}
FutureTask#get:获取任务执行的返回值,执行 run 和 get 的不是同一个线程,一般有多个线程 get,只有一个线程 run
public V get() throws InterruptedException, ExecutionException {
// 获取当前任务状态
int s = state;
// 条件成立说明任务还没执行完成
if (s <= COMPLETING)
// 返回 task 当前状态,可能当前线程在里面已经睡了一会
s = awaitDone(false, 0L);
return report(s);
}
FutureTask#awaitDone:get 线程封装成 WaitNode 对象进入阻塞队列阻塞等待
private int awaitDone(boolean timed, long nanos) throws InterruptedException {
// 0 不带超时
final long deadline = timed ? System.nanoTime() + nanos : 0L;
// 引用当前线程,封装成 WaitNode 对象
WaitNode q = null;
// 表示当前线程 waitNode 对象,是否进入阻塞队列
boolean queued = false;
// 【三次自旋开始休眠】
for (;;) {
// 判断当前 get() 线程是否被打断,打断返回 true,清除打断标记
if (Thread.interrupted()) {
// 当前线程对应的等待 node 出队,
removeWaiter(q);
throw new InterruptedException();
}
// 获取任务状态
int s = state;
// 条件成立说明当前任务执行完成已经有结果了
if (s > COMPLETING) {
// 条件成立说明已经为当前线程创建了 WaitNode,置空 help GC
if (q != null)
q.thread = null;
// 返回当前的状态
return s;
}
// 条件成立说明当前任务接近完成状态,这里让当前线程释放一下 cpu ,等待进行下一次抢占 cpu
else if (s == COMPLETING)
Thread.yield();
// 【第一次自旋】,当前线程还未创建 WaitNode 对象,此时为当前线程创建 WaitNode对象
else if (q == null)
q = new WaitNode();
// 【第二次自旋】,当前线程已经创建 WaitNode 对象了,但是node对象还未入队
else if (!queued)
// waiters 指向队首,让当前 WaitNode 成为新的队首,【头插法】,失败说明其他线程修改了新的队首
queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q);
// 【第三次自旋】,会到这里,或者 else 内
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
// 阻塞指定的时间
LockSupport.parkNanos(this, nanos);
}
// 条件成立:说明需要阻塞
else
// 【当前 get 操作的线程被 park 阻塞】,除非有其它线程将唤醒或者将当前线程中断
LockSupport.park(this);
}
}
FutureTask#report:封装运行结果,可以获取 run() 方法中设置的成员变量 outcome,这是 run 方法的返回值是 void 也可以获取到任务执行的结果的原因
private V report(int s) throws ExecutionException {
// 获取执行结果,是在一个 futuretask 对象中的属性,可以直接获取
Object x = outcome;
// 当前任务状态正常结束
if (s == NORMAL)
return (V)x; // 直接返回 callable 的逻辑结果
// 当前任务被取消或者中断
if (s >= CANCELLED)
throw new CancellationException(); // 抛出异常
// 执行到这里说明自定义的 callable 中的方法有异常,使用 outcome 上层抛出异常
throw new ExecutionException((Throwable)x);
}
FutureTask#cancel:任务取消,打断正在执行该任务的线程
public boolean cancel(boolean mayInterruptIfRunning) {
// 条件一:表示当前任务处于运行中或者处于线程池任务队列中
// 条件二:表示修改状态,成功可以去执行下面逻辑,否则返回 false 表示 cancel 失败
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try {
// 如果任务已经被执行,是否允许打断
if (mayInterruptIfRunning) {
try {
// 获取执行当前 FutureTask 的线程
Thread t = runner;
if (t != null)
// 打断执行的线程
t.interrupt();
} finally {
// 设置任务状态为【中断完成】
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
// 唤醒所有 get() 阻塞的线程
finishCompletion();
}
return true;
}
详细介绍FutureTask类的更多相关文章
- java中的compareto方法以及LIst列表排序的详细介绍【转】
java中的compareto方法的详细介绍 javacompareTo java中的compareto方法,返回参与比较的前后两个字符串的asc码的差值,看下面一组代码 String a=&quo ...
- 并发系列(二)——FutureTask类源码简析
背景 本文基于JDK 11,主要介绍FutureTask类中的run().get()和cancel() 方法,没有过多解析相应interface中的注释,但阅读源码时建议先阅读注释,明白方法的主要的功 ...
- 第八节:详细讲解Java中的异常处理情况与I/O流的介绍以及类集合框架
前言 大家好,给大家带来详细讲解Java中的异常处理情况与I/O流的介绍以及类集合框架的概述,希望你们喜欢 JAVA 异常 try...catch...finally结构的使用方法 class Tes ...
- IO流分类详细介绍和各种字节流类介绍与使用 过滤流 字节流
Java基础笔记 – IO流分类详细介绍和各种字节流类介绍与使用 过滤流 字节流本文由 arthinking 发表于627 天前 ⁄ Java基础 ⁄ 评论数 1 ⁄ 被围观 2,036 views+ ...
- [No0000A7]批处理经常用到的变量及批处理>NUL详细介绍
绝对路径是指调用绝对的程序位置的路径,例如: start C:\Windows\test.exe 相对路径是文件改变路径以后还会按照变量的路径所在位置去调用,例如: start %WINDIR%\te ...
- cPage分页详细介绍
asp.net中各种数据控件,datalist.gridview.Repeater等分页是最常用的功能,几乎任何一个B/S项目,无论是系统还是网站都会用到.分页时,读取整个数据,直接绑定到控件,都可以 ...
- Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例
概要 前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...
- Java 集合系列10之 HashMap详细介绍(源码解析)和使用示例
概要 这一章,我们对HashMap进行学习.我们先对HashMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashMap.内容包括:第1部分 HashMap介绍第2部分 HashMa ...
- Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例
概要 前一章,我们学习了HashMap.这一章,我们对Hashtable进行学习.我们先对Hashtable有个整体认识,然后再学习它的源码,最后再通过实例来学会使用Hashtable.第1部分 Ha ...
- Java 集合系列12之 TreeMap详细介绍(源码解析)和使用示例
概要 这一章,我们对TreeMap进行学习.我们先对TreeMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用TreeMap.内容包括:第1部分 TreeMap介绍第2部分 TreeMa ...
随机推荐
- 首批!天翼云率先通过ITU国际标准认证!
近日,天翼云通过国内唯一人工智能云平台领域的ITU国际标准评估--中国信通院组织的ITU-T F.AICP-GA人工智能云平台技术规范国际标准和<智算工程平台能力要求>国内标准一致性评估, ...
- 表治理-Iceberg小文件合并测试
总结 指标 合并前 合并后(因测试中多次合并,数据会偏多) 查询速度 246秒 13秒 表总大小 9.2G 26.4G 单个文件大小 1-25MB 60MB左右 metadata目录文件数 37 75 ...
- 定制化训练DeepSeek模型:LoAR、COT推理与SFT技术应用
DeepSeek-R1 模型微调系列 DeepSeek-R1 模型微调系列一. 前言介绍本文内容:1.1 项目背景1.2 LoRA和 QLoRA 简介1.3 LLaMA 架构和 Qwen 架构LLaM ...
- bash 学习
学习bash shell 第一天 在百度百科上找的解释 Bash,Unix shell的一种,在1987年由布莱恩·福克斯为了GNU计划而编写.1989年发布第一个正式版本,原先是计划用在GNU操作系 ...
- docker - [12] 镜像发布到DockerHub、阿里云
题记部分 一.镜像发布到 DockerHub 1.地址:https://hub.docker.com/ 注册自己的账号 2.确定这个账号可以登录 3.在服务器上提交镜像 4.登录之后提交镜像即可. [ ...
- Scala变量补充
package com.wyh.day01 object ScalaLanguage { def main(args: Array[String]): Unit = { /** * 定义变量和常量 * ...
- Scala面向对象之创建对象,重载构造方法,继承抽象类实现接口
package com.wyh.day01 object ScalaClass { def main(args: Array[String]): Unit = { val student = new ...
- 面试题30. 包含min函数的栈
地址:https://leetcode-cn.com/problems/bao-han-minhan-shu-de-zhan-lcof/ <?php /** 定义栈的数据结构,请在该类型中实现一 ...
- 多机器的键鼠互通——Synergy/Deskflow配置记录
Synergy (1.14.6) 情况一样,那么感觉就是机器之间TCP连接有问题,测试不同 一些测试命令 ss -tlnp | grep 24800 # 查看端口情况 sudo lsof -i :24 ...
- 探秘Transformer系列之(14)--- 残差网络和归一化
探秘Transformer系列之(14)--- 残差网络和归一化 目录 探秘Transformer系列之(14)--- 残差网络和归一化 0x00 概述 0x01 残差连接 1.1 问题 1.2 相关 ...