当前位置: 首页 > news >正文

FutureTask配合Thread实现处理有返回结果的源码、逻辑与架构分析

文章目录

  • 1.介绍
  • 2.使用示例
  • 3.执行过程描述
  • 4.整体的关系
  • 5.涉及到的核心源码(只提取了关键代码)
    • 5.1 Callable
    • 5.2 RunnableFuture
    • 5.3 FutureTask
    • 5.4 Thread

1.介绍

FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况。

2.使用示例

// 创建任务对象
FutureTask<Integer> task = new FutureTask<>(() -> {log.debug("running");Thread.sleep(1000);return 200;
});new Thread(task).start();// 主线程阻塞,同步等待 task 执行完毕的结果
Integer value = task.get();System.out.println("value = " + value);

3.执行过程描述

  1. FutureTask 类在实例化构造时需要传入一个实现了 Callable 接口的类,实现 Callable 接口需要重写 call 方法,该方法需要一个返回值,由于 Callable 定义时是以泛型定义返回值,因此我们可以自定义返回值。FutureTask 会将传入的这个 Callable 实现类赋给自己的属性 private Callable<V> callable;
  2. FutureTask 间接实现了 Runnable 接口,并重写了 run 方法,重写的 run 方法中会调用到 属性 callable 的 call 方法,并将 call 方法返回值存储到自己的属性 private Object outcome;
  3. Thread 类在实例化构造时可以传入一个 Runnable 接口的类,由于 FutureTask 实现了 Runnable 接口,因此我们可以直接将 FutureTask 对象作为构造器实参赋给 Thread对象的属性 private Runnable target;
  4. Thread 对象调用 start 方法,最终会调用到自身就重写了的 run 方法,自身重写的 run 方法中又会调用到 target 的 run 方法,即 FutureTask 自身已经重写的 run 方法,这时候就可以回到“第 2 点讲解”,了解到 FutureTask 的 run 方法中所做的事情。
  5. FutureTask 对象的 get() 方法,是去获取 callable 的 call 方法返回值,即属性 outcome 的值。get 方法中会调用 awaitDone 方法,awaitDone 方法中会使用 for (;;) 造成当前线程阻塞,直到 call 方法执行结束可以获取到 outcome 的值,并将 outcome 作为 get() 方法返回值。

4.整体的关系

Thread 和 FutureTask 类均实现了 Runnable 接口并重写了其 run 方法,Thread 将 FutureTask 进行聚合赋给 private Runnable target

5.涉及到的核心源码(只提取了关键代码)

5.1 Callable

@FunctionalInterface
public interface Callable<V> {/*** Computes a result, or throws an exception if unable to do so.** @return computed result* @throws Exception if unable to compute a result*/V call() throws Exception;
}

5.2 RunnableFuture

public interface RunnableFuture<V> extends Runnable, Future<V> {/*** Sets this Future to the result of its computation* unless it has been cancelled.*/void run();
}

5.3 FutureTask

public class FutureTask<V> implements RunnableFuture<V> {/** The underlying callable; nulled out after running */private Callable<V> callable;// 存储 callable 接口的 call 方法的返回值/** The result to return or exception to throw from get() */private Object outcome; // non-volatile, protected by state reads/writes/*() -> {log.debug("running");Thread.sleep(1000);return 200;}这实际上是对函数式接口 callable 的 V call() 方法进行实现*/public FutureTask(Callable<V> callable) {if (callable == null)throw new NullPointerException();this.callable = callable;this.state = NEW;       // ensure visibility of callable}   public void run() {// ...Callable<V> c = callable;// 重写了 Runnable 函数式接口的 run 方法result = c.call();// ...// 赋值set(result);// ...}protected void set(V v) {if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {// 将 callable 的 call 方法返回值,即我们自定义的 200 赋给 outcomeoutcome = v;UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final statefinishCompletion();}}// 获取 callable 的 call 方法的返回结果public V get() throws InterruptedException, ExecutionException {int s = state;if (s <= COMPLETING)// 获取到结果成功的标识,实际是在 awaitDone 方法中用了死循环不断判断是否生成返回结果,造成了线程阻塞s = awaitDone(false, 0L);// 获取结果return report(s);}// timed-是否计时等待,即是否设置等待超时,false表示不设置,true表示设置private int awaitDone(boolean timed, long nanos)throws InterruptedException {final long deadline = timed ? System.nanoTime() + nanos : 0L;WaitNode q = null;boolean queued = false;// 死循环for (;;) {if (Thread.interrupted()) {removeWaiter(q);throw new InterruptedException();}int s = state;if (s > COMPLETING) {if (q != null)q.thread = null;return s;}else if (s == COMPLETING) // cannot time out yetThread.yield();else if (q == null)q = new WaitNode();else if (!queued)queued = UNSAFE.compareAndSwapObject(this, waitersOffset,q.next = waiters, q);else if (timed) {nanos = deadline - System.nanoTime();if (nanos <= 0L) {removeWaiter(q);return state;}LockSupport.parkNanos(this, nanos);}elseLockSupport.park(this);}}
}

5.4 Thread

public class Thread implements Runnable {/* What will be run. */private Runnable target;// 构造器,将间接实现了 Runnable 接口的 FutureTask 对象传进来public Thread(Runnable target) {init(null, target, "Thread-" + nextThreadNum(), 0);}private void init(ThreadGroup g, Runnable target, String name, long stackSize) {init(g, target, name, stackSize, null, true);}private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {// ...// 将 FutureTask 对象赋给 Thread 对象的属性 targetthis.target = target;}@Overridepublic void run() {if (target != null) {// 实际调用的 FutureTask 对象重写的 run 方法,重写的 run 方法中又会调用 callable 接口的 call 方法,并将 call 方法的返回值赋给 FutureTask 对象的属性 outcometarget.run();}}
}
http://www.lryc.cn/news/199888.html

相关文章:

  • Queue Deque 介绍
  • 机器学习(23)---Boosting tree(课堂笔记)
  • Excel 导出打不开
  • css钟表数字样式
  • 一步一步分析ChatGPT,1 粘性,2 传染性, 3 双边网络效应
  • Arthas(阿尔萨斯):阿里巴巴开源的线上问题诊断工具
  • 由Django-Session配置引发的反序列化安全问题
  • 16-spring AOP核心对象的创建
  • Golang 泛型的介绍
  • RK3568笔记四:基于TensorFlow花卉图像分类部署
  • 甄知科技张礼军:数智化转型助企业破茧成蝶!
  • Golang Map:高效的键值对容器
  • 2023年【电工(高级)】报名考试及电工(高级)模拟考试题
  • 伊朗相关的OilRig组织在为期8个月的网络攻击中针对中东政府
  • 服务器数据恢复-linux+raid+VMwave ESX数据恢复案例
  • 残疾人求助报警器
  • 【Datawhale】扩散模型学习笔记 第一次打卡
  • Spring Boot学习笔记
  • 图像边缘检测--(Sobel、Laplacian、Canny)
  • 【计算机网络笔记】计算机网络性能(2)——时延带宽积、丢包率、吞吐量/率
  • 自学(黑客技术)——网络安全高效学习方法
  • 【Linux】进程概念与进程状态
  • 解决安装nvm以后windows cmd无法找到npm/yarn命令的问题
  • 深入解析Java正则表达式:定义、原理和实例
  • DatenLord前沿技术分享 No.38
  • ms-sql server sql 把逗号分隔的字符串分开
  • 零基础制作预约小程序,微信小程序预约服务指南
  • 算法---交替合并字符串
  • 下载运行ps软件提示因为计算机中丢失d3dcompiler_47.dll解决方法
  • Flutter Image组件如何处理图片加载过程中的错误?