一、详细介绍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类的更多相关文章

  1. java中的compareto方法以及LIst列表排序的详细介绍【转】

    java中的compareto方法的详细介绍 javacompareTo  java中的compareto方法,返回参与比较的前后两个字符串的asc码的差值,看下面一组代码 String a=&quo ...

  2. 并发系列(二)——FutureTask类源码简析

    背景 本文基于JDK 11,主要介绍FutureTask类中的run().get()和cancel() 方法,没有过多解析相应interface中的注释,但阅读源码时建议先阅读注释,明白方法的主要的功 ...

  3. 第八节:详细讲解Java中的异常处理情况与I/O流的介绍以及类集合框架

    前言 大家好,给大家带来详细讲解Java中的异常处理情况与I/O流的介绍以及类集合框架的概述,希望你们喜欢 JAVA 异常 try...catch...finally结构的使用方法 class Tes ...

  4. IO流分类详细介绍和各种字节流类介绍与使用 过滤流 字节流

    Java基础笔记 – IO流分类详细介绍和各种字节流类介绍与使用 过滤流 字节流本文由 arthinking 发表于627 天前 ⁄ Java基础 ⁄ 评论数 1 ⁄ 被围观 2,036 views+ ...

  5. [No0000A7]批处理经常用到的变量及批处理>NUL详细介绍

    绝对路径是指调用绝对的程序位置的路径,例如: start C:\Windows\test.exe 相对路径是文件改变路径以后还会按照变量的路径所在位置去调用,例如: start %WINDIR%\te ...

  6. cPage分页详细介绍

    asp.net中各种数据控件,datalist.gridview.Repeater等分页是最常用的功能,几乎任何一个B/S项目,无论是系统还是网站都会用到.分页时,读取整个数据,直接绑定到控件,都可以 ...

  7. Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例

    概要  前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...

  8. Java 集合系列10之 HashMap详细介绍(源码解析)和使用示例

    概要 这一章,我们对HashMap进行学习.我们先对HashMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashMap.内容包括:第1部分 HashMap介绍第2部分 HashMa ...

  9. Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例

    概要 前一章,我们学习了HashMap.这一章,我们对Hashtable进行学习.我们先对Hashtable有个整体认识,然后再学习它的源码,最后再通过实例来学会使用Hashtable.第1部分 Ha ...

  10. Java 集合系列12之 TreeMap详细介绍(源码解析)和使用示例

    概要 这一章,我们对TreeMap进行学习.我们先对TreeMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用TreeMap.内容包括:第1部分 TreeMap介绍第2部分 TreeMa ...

随机推荐

  1. 首批!天翼云率先通过ITU国际标准认证!

    近日,天翼云通过国内唯一人工智能云平台领域的ITU国际标准评估--中国信通院组织的ITU-T F.AICP-GA人工智能云平台技术规范国际标准和<智算工程平台能力要求>国内标准一致性评估, ...

  2. 表治理-Iceberg小文件合并测试

    总结 指标 合并前 合并后(因测试中多次合并,数据会偏多) 查询速度 246秒 13秒 表总大小 9.2G 26.4G 单个文件大小 1-25MB 60MB左右 metadata目录文件数 37 75 ...

  3. 定制化训练DeepSeek模型:LoAR、COT推理与SFT技术应用

    DeepSeek-R1 模型微调系列 DeepSeek-R1 模型微调系列一. 前言介绍本文内容:1.1 项目背景1.2 LoRA和 QLoRA 简介1.3 LLaMA 架构和 Qwen 架构LLaM ...

  4. bash 学习

    学习bash shell 第一天 在百度百科上找的解释 Bash,Unix shell的一种,在1987年由布莱恩·福克斯为了GNU计划而编写.1989年发布第一个正式版本,原先是计划用在GNU操作系 ...

  5. docker - [12] 镜像发布到DockerHub、阿里云

    题记部分 一.镜像发布到 DockerHub 1.地址:https://hub.docker.com/ 注册自己的账号 2.确定这个账号可以登录 3.在服务器上提交镜像 4.登录之后提交镜像即可. [ ...

  6. Scala变量补充

    package com.wyh.day01 object ScalaLanguage { def main(args: Array[String]): Unit = { /** * 定义变量和常量 * ...

  7. Scala面向对象之创建对象,重载构造方法,继承抽象类实现接口

    package com.wyh.day01 object ScalaClass { def main(args: Array[String]): Unit = { val student = new ...

  8. 面试题30. 包含min函数的栈

    地址:https://leetcode-cn.com/problems/bao-han-minhan-shu-de-zhan-lcof/ <?php /** 定义栈的数据结构,请在该类型中实现一 ...

  9. 多机器的键鼠互通——Synergy/Deskflow配置记录

    Synergy (1.14.6) 情况一样,那么感觉就是机器之间TCP连接有问题,测试不同 一些测试命令 ss -tlnp | grep 24800 # 查看端口情况 sudo lsof -i :24 ...

  10. 探秘Transformer系列之(14)--- 残差网络和归一化

    探秘Transformer系列之(14)--- 残差网络和归一化 目录 探秘Transformer系列之(14)--- 残差网络和归一化 0x00 概述 0x01 残差连接 1.1 问题 1.2 相关 ...