【Java】浅析FutureTask的核心方法get

前言

        在进行多线程编程时,我们离不开两个重要的任务接口:RunnableCallable。一个线程想要运行,首先它得知道它的任务是什么(它要做什么),而这两个接口恰好是用于表示一个线程需要执行的任务。

        Runnable和Callable两个接口都是任务接口,它们之间有何不同呢?Runnable中的run方法是没有返回值的,而Callable中的call方法有返回值V(泛型);同时call方法支持抛出异常,一般情况下我们都会使用Runnable接口,当需要线程的执行结果时就使用Callable接口。

public interface Runnable {public abstract void run();
}public interface Callable<V> {V call() throws Exception;
}

        那么我们如何获取一个线程的执行结果呢?此时就需要用到Future接口及其实现类FutureTask了。Future接口中有一个get()方法,用于同步获取线程执行结果,同步表示当线程还没有执行完任务时,调用get()方法获取执行结果的线程会阻塞,直至线程返回执行结果。

FutureTask源码解读

        1、FutureTask不直接实现Future接口,而是实现了一个RunnableFuture<V>接口,RunnableFuture<V>接口又继承自Runnable、Future接口,属于这两个接口的子接口,所以FutureTask不仅可以用来同步获取线程执行结果,还可以作为任务提交给线程执行


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

        2、FutureTask中的字段:

    //任务的执行状态(get方法的关键)private volatile int state;//任务刚被创建private static final int NEW          = 0;//任务正在处理private static final int COMPLETING   = 1;//任务正常完成private static final int NORMAL       = 2;//任务执行过程中出现异常private static final int EXCEPTIONAL  = 3;//任务被取消private static final int CANCELLED    = 4;//任务执行过程中被打断private static final int INTERRUPTING = 5;private static final int INTERRUPTED  = 6;//任务private Callable<V> callable;//任务执行结果private Object outcome; //执行任务的线程private volatile Thread runner;//链表结构:保存等待线程private volatile WaitNode waiters;

        2.1、WaitNode具体实现:

static final class WaitNode {//结点存储的线程volatile Thread thread;//结点的next指针volatile WaitNode next;WaitNode() { thread = Thread.currentThread(); }}

        3、FutureTask的构造方法:

//需要传递一个Callable接口的实现类
//在FutureTask作为任务提交给线程时,执行的是实现类实现的call方法
public FutureTask(Callable<V> callable) {//如果入参的Callable为null,抛出异常if (callable == null)throw new NullPointerException();//不然赋值给成员变量//并将FutureTask状态设置为NEWthis.callable = callable;this.state = NEW;       // ensure visibility of callable}/**需要传递两个参数:Runnable、Result前者是FutureTask作为任务提交给线程时,线程的执行逻辑后者是线程在任务完成时,需要get方法返回的结果result入参可以为null,表示不需要给定的结果
*/
public FutureTask(Runnable runnable, V result) {//callable方法会将runnable、result封装成一个callablethis.callable = Executors.callable(runnable, result);//将FutureTask状态设置为NEWthis.state = NEW;       // ensure visibility of callable}//将Runnable封装成Callable返回
public static <T> Callable<T> callable(Runnable task, T result) {if (task == null)throw new NullPointerException();return new RunnableAdapter<T>(task, result);}//RunnableAdapter实现了Callable,所以也可以作为任务交给线程执行。
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() {task.run();return result;}}

        4、获取执行结果的get()方法:

public V get() throws InterruptedException, ExecutionException {int s = state;if (s <= COMPLETING)//如果任务是刚被创建(NEW)、或者是正在处理(COMPLETING)状态//说明任务还未被处理完毕,阻塞获取任务执行结果的线程s = awaitDone(false, 0L);//如果任务是其它状态,说明任务已经处理完毕//report方法会根据任务状态返回结果给调用get方法的线程return report(s);}

        4.1、report()方法:

private V report(int s) throws ExecutionException {Object x = outcome;if (s == NORMAL)//任务状态为NORMAL,正常返回结果return (V)x;if (s >= CANCELLED)//任务状态为CANCELLED、INTERRUPTING、INTERRUPTED其中之一//抛出CancellationException异常throw new CancellationException();//任务状态为EXCEPTIONAL,抛出ExecutionException异常throw new ExecutionException((Throwable)x);}

        4.2、awaitDone()方法:

private int awaitDone(boolean timed, long nanos)throws InterruptedException {//deadline变量赋值分两种情况//调用了get()方法:deadline = 0//调用了get(long timeout,TimeUnit unit)方法:deadline = 当前时间 + timeoutfinal long deadline = timed ? System.nanoTime() + nanos : 0L;WaitNode q = null;boolean queued = false;for (;;) {//如果当前线程被打断了//就将其从等待链表中移除,并抛出InterruptedException异常if (Thread.interrupted()) {removeWaiter(q);throw new InterruptedException();}//获取当前任务的状态int s = state;//任务已完成(正常完成、取消、打断都算完成)if (s > COMPLETING) {//如果有线程在等待,就将线程设置为null,防止内存溢出if (q != null)q.thread = null;//返回任务状态return s;}//任务正在处理//当前等待执行结果的线程调用yield方法让os将CPU时间片分给其它线程else if (s == COMPLETING) // cannot time out yetThread.yield();//任务刚被创建,处于NEW状态//若等待节点q为null,则创建一个等待节点else if (q == null)q = new WaitNode();//如果当前等待节点还未加入等待队列,则通过CAS操作将其加入等待队列else if (!queued)queued = UNSAFE.compareAndSwapObject(this, waitersOffset,q.next = waiters, q);//如果设置了超时时间,则计算等待时间//1.等待时间 >= 超时时间,那么将等待节点移除,并返回任务状态//2.等待时间 < 超时时间,那么就阻塞:超时时间 - 等待时间else if (timed) {nanos = deadline - System.nanoTime();if (nanos <= 0L) {removeWaiter(q);return state;}LockSupport.parkNanos(this, nanos);}//如果没有设置超时时间,通过LockSupport无时间限制的阻塞当前线程elseLockSupport.park(this);}}

        4.3、get方法流程图:

get方法测试:

public class FutureTaskTest {public static void main(String[] args) throws ExecutionException, InterruptedException {//通过匿名内部类的形式实现call方法Callable<String> callable = new Callable<String>() {@Overridepublic String call() throws Exception {//模拟线程执行任务耗时Thread.sleep(3000);return "task is completed!";}};FutureTask<String> task = new FutureTask<>(callable);long begin = System.currentTimeMillis();//将任务task分配给worker线程Thread worker = new Thread(task);worker.start();//主线程获取worker线程的执行结果String result = task.get();long end = System.currentTimeMillis();System.out.println("等待" + (end - begin) + "ms后获取到执行结果:" + result);}
}

        执行结果:

补充:

        在分析FutureTask构造方法时,第二个构造方法很有意思。RunnableAdapter只负责组合Runnable,并实现Callable接口,call()方法的逻辑由作为成员变量的Runnable实现,这里有点适配器模式的味道。如下图所示:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/189413.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SHAP(三):在解释预测模型以寻求因果见解时要小心

SHAP&#xff08;三&#xff09;&#xff1a;在解释预测模型以寻求因果见解时要小心 与 Microsoft 的 Eleanor Dillon、Jacob LaRiviere、Scott Lundberg、Jonathan Roth 和 Vasilis Syrgkanis 合作撰写的关于因果关系和可解释机器学习的文章。 当与 SHAP 等可解释性工具配合…

Mac 安装 Django 并连接 MySQL

一、下载安装运行Django看官方教程就好了&#xff0c;网址&#xff1a;Django 安装_w3cschool 二、连接MySQL&#xff08;我用的是pymysql和mysqlclient&#xff09;&#xff1a; 1、创建好项目后找到这个文件 2、修改当中的连接信息&#xff0c;将这些信息改成你自己的就好了…

2023-12-01 AIGC-自动生成ppt的AI工具

摘要: 2023-12-01 AIGC-自动生成ppt-记录 自动生成ppt: BoardMix boardmix 一键生成ppt boardmix是一款基于云的ai设计软件&#xff0c;允许创建用于各种目的的自定义演示文稿、ai绘画&#xff0c;ai生成思维导图等。以下是它的一些功能&#xff1a; 可定制的模板 - 它有一个…

Redis基本命令

文章目录 第1关&#xff1a;字符串、列表与集合第2关&#xff1a;哈希与有序集合第3关&#xff1a;Redis基本事务与其他命令 第1关&#xff1a;字符串、列表与集合 编程要求 根据提示&#xff0c;在右侧Begin-End区域补充代码&#xff0c;完成任务分配的后端处理逻辑&#xff…

Python-docx 深入word源码 自定义页码页脚以动态显示总页数和当前页数

代码和效果图 先上能够正常显示页码页脚的Python代码和效果图&#xff0c;之后再解释原理和思路 from docx import Document from docx.shared import Pt from docx.oxml import OxmlElement from docx.enum.text import WD_PARAGRAPH_ALIGNMENT from docx.oxml.ns import qn…

Elasticsearch:什么是非结构化数据?

非结构化数据定义 非结构化数据是指未按照设计的模型或结构组织的数据。 非结构化数据通常被归类为定性数据&#xff0c;可以是人类或机器生成的。 非结构化数据是最丰富的可用数据类型&#xff0c;经过分析后&#xff0c;可用于指导业务决策并在许多其他用例中实现业务目标。…

java原子类型

AtomicBoolean AtomicInteger AtomicLong AtomicReference<V> StringBuilder - 不是原子类型。StringBuilder 是 java.lang 包下的类 用法&#xff1a;无需回调改变数值

【学习记录】从0开始的Linux学习之旅——应用开发(helloworld)

一、概述 Linux操作系统通常是基于Linux内核&#xff0c;并结合GNU项目中的工具和应用程序而成。Linux操作系统支持多用户、多任务和多线程&#xff0c;具有强大的网络功能和良好的兼容性。本文主要讲述如何在linux系统上进行应用开发。 二、概念及原理 应用程序通过系统调用与…

今日现货黄金最新建议

近期现货黄金价格再度逼近历史高位&#xff0c;很多本来在场外观望的投资者&#xff0c;都纷纷希望进场一试身手。然而大涨大跌的行情并不是很适合新手投资者参与&#xff0c;如果大家还没做好技术上的准备&#xff0c;可以多听听正规交易平台的专业人士的意见。 在正式入市之前…

【LeetCode每日一题合集】2023.11.20-2023.11.26 (二叉树中的伪回文路径)

文章目录 53. 最大子数组和解法1——DP解法2——分治&#xff08;维护区间、类似线段树的思想&#xff09; 2216. 美化数组的最少删除数&#xff08;贪心&#xff09;2304. 网格中的最小路径代价1410. HTML 实体解析器&#xff08;模拟&#xff09;2824. 统计和小于目标的下标对…

jsp前端输入中文数据传到controller变成问号?的解决办法

还是写老师布置的实验的时候&#xff0c;解决了xml文件找不到的问题之后又遇到新的问题&#xff1a;前端登录处输入用户名和密码&#xff0c;结果明明输入的用户名是对的密码也是对的&#xff08;输入的用户名是中文&#xff09;&#xff0c;它就是显示用户名或密码错误。然后我…

mac 系统 vmware 安装centos8

选择镜像 安装系统 依次设置有告警的项目 设置用户名密码 设置root密码 重启系统 重启成功进入下面界面 勾选&#xff0c;点击done 点击箭头所指按钮 输入密码登录 安装成功了 设置网络 打开终端 切换root用户 输入下面指令 su root 输入root的密码 安装git

【Cisco Packet Tracer】交换机 学习/更新/泛洪/VLAN实验

交换机的功能是连接计算机、服务器、网络打印机、网络摄像头、IP电话等终端设备&#xff0c;并实现与其它交换机、无线接入点、路由器、网络防火墙等网络设备的互联&#xff0c;从而构建局域网络&#xff0c;实现所有设备之间的通信。 本文使用Cisco Packet Tracer仿真软件&…

DynamicDataSource

DynamicDataSource 多数据源&#xff0c;读写分离&#xff0c;主从数据库

算法题--排椅子(贪心)

题目链接 code #include<bits/stdc.h> using namespace std;struct node{int indx;//用来存储数组下标int cnt;//用来计数 };bool cmp(node a,node b){ //判断是否是数字最大的一个就是经过最多谈话人的道return a.cnt>b.cnt; } node row[2010],cow[2010];bool cmp…

润和软件HopeStage与深信服终端安全管理系统完成产品兼容性互认证

近日&#xff0c;江苏润和软件股份有限公司&#xff08;以下简称“润和软件”&#xff09;HopeStage 操作系统与深信服科技股份有限公司&#xff08;以下简称“深信服”&#xff09;终端安全管理系统完成产品兼容性测试。 测试结果表明&#xff0c;企业级通用操作系统HopeStage…

Elasticsearch 优化查询中获取字段内容的方式,性能提升5倍!

1、背景 集群配置为&#xff1a;8 个 node 节点&#xff0c;16 核 32G&#xff0c;索引 4 分片 1 副本。应用程序的查询逻辑是按经纬度排序后找前 200 条文档。 1、应用对查询要求比较高&#xff0c;search 没有慢查询的状态。 2、集群压测性能不能上去&#xff0c;cpu 使用未打…

【已解决】如何打开设置了密码的7Z压缩文件?

7Z是一种常见的压缩文件格式&#xff0c;相比RAR和ZIP格式&#xff0c;它的压缩率更高&#xff0c;可以压缩出更小的文件体积&#xff0c;也同样可以设置密码保护&#xff0c;那设置了密码的7Z压缩文件要如何打开呢&#xff1f; 我们知道&#xff0c;7Z压缩文件设置密码保护后…

【Redis】Redis高并发高可用(集群方案)

Redis集群 Redis Cluster是Redis的分布式解决方案,在3.0版本正式推出,有效地解决了Redis分布式方面的需求。当遇到单机内存、并发、流量等瓶颈时,可以采用Cluster架构方案达到负载均衡的目的。之前,Redis分布式方案一般有两种: 1、客户端分区方案,优点是分区逻辑可控,缺…

2024 年甘肃省职业院校技能大赛中职组 电子与信息类“网络安全”赛项竞赛样题-C卷

2024 年甘肃省职业院校技能大赛中职组 电子与信息类“网络安全”赛项竞赛样题-C卷 2024 年甘肃省职业院校技能大赛中职组 电子与信息类“网络安全”赛项竞赛样题-C卷A模块基础设施设置/安全加固&#xff08;200分&#xff09;A 模块基础设施设置/安全加固&#xff08;200 分&am…