java八股文面试[多线程]——FutureTask

news2025/2/21 23:37:01
FutureTask介绍

FutureTask是一个可以取消异步任务的类。FutureTask对Future做的一个基本实现。可以调用方法去开始和取消一个任务。

一般是配合Callable去使用。

异步任务启动之后,可以获取一个绑定当前异步任务的FutureTask。

可以基于FutureTask的方法去取消任务,查看任务是否结果,以及获取任务的返回结果

FutureTask内部的整体结构中,实现了RunnableFuture的接口,这个接口又继承了Runnable, Future这个两个接口。所以FutureTask也可以作为任务直接交给线程池去处理。

FutureTask应用

大方向是FutureTask对任务的控制:

* 任务执行过程中状态的控制
* 任务执行完毕后,返回结果的获取

FutureTask的任务在执行run方法后,是无法被再次运行,需要使用runAndReset方法才可以。

FutureTask源码分析

看FutureTask的源码,要从几个方向去看:

* 先查看FutureTask中提供的一些状态
* 在查看任务的执行过程

FutureTask中的核心属性

清楚任务的流转状态是怎样的,其次对于核心属性要追到是干嘛的。

/**
 FutureTask的核心属性
 FutureTask任务的状态流转
 * NEW -> COMPLETING -> NORMAL           任务正常执行,并且返回结果也正常返回
 * NEW -> COMPLETING -> EXCEPTIONAL      任务正常执行,但是结果是异常
 * NEW -> CANCELLED                      任务被取消   
 * NEW -> INTERRUPTING -> INTERRUPTED    任务被中断
 */
// 记录任务的状态
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; // non-volatile, protected by state reads/writes
/** 执行任务的线程 */
private volatile Thread runner;
/** 等待返回结果的线程Node对象, */
private volatile WaitNode waiters;
static final class WaitNode {
    volatile Thread thread;
    volatile WaitNode next;
    WaitNode() { thread = Thread.currentThread(); }
}

 FutureTask的run方法

任务执行前的一些判断,以及调用任务封装结果的方式,还有最后的一些后续处理

// 当线程池执行FutureTask任务时,会调用的方法
public void run() {
    // 如果当前任务状态不是NEW,直接return告辞
    if (state != NEW ||  
        // 如果状态正确是NEW,这边需要基于CAS将runner属性设置为当前线程
        // 如果CAS失败,直接return告辞
        !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
        return;

    try {
        // 将要执行的任务拿到
        Callable<V> c = callable;
        // 健壮性判断,保证任务不是null
        // 再次判断任务的状态是NEW(DCL)
        if (c != null && state == NEW) {
            // 执行任务
            // result:任务的返回结果
            // ran:如果为true,任务正常结束。 如果为false,任务异常结束。
            V result;
            boolean ran;
            try {
                // 执行任务
                result = c.call();
                // 正常结果,ran设置为true
                ran = true;
            } catch (Throwable ex) {
                // 如果任务执行期间出了异常
                // 返回结果置位null
                result = null;
                // ran设置为false
                ran = false;
                // 封装异常结果
                setException(ex);
            }
            if (ran)
                // 封装正常结果
                set(result);
        }
    } finally {
        // 将执行任务的线程置位null
        runner = null;
        // 拿到任务的状态
        int s = state;
        // 如果状态大于等于INTERRUPTING
        if (s >= INTERRUPTING)
            // 进来代表任务中断,做一些后续处理
            handlePossibleCancellationInterrupt(s);
    }
}

当FutureTask处于未启动或者已启动的状态时,调用FutureTask对象的get方法会将导致调用线程阻塞

当FutureTask处于已完成的状态时,调用FutureTask的get方法会立即放回调用结果或者抛出异常
当FutureTask处于未启动状态时,调用FutureTask对象的cancel方法将导致线程永远不会被执行;当FutureTask处于已启动状态时,调用FutureTask对象cancel(true)方法将以中断执行此任务的线程的方式来试图停止此任务;

当FutureTask处于已启动状态时,调用FutureTask对象cancel(false)方法将不会对正在进行的任务产生任何影响;

当FutureTask处于已完成状态时,调用FutureTask对象cancel方法将返回false
FutureTask的get和cancel的执行示意图

FutureTask使用
可以把FutureTask交给Executor执行;也可以通ExecutorService.submit(…)方法返回一个FutureTask,然后执行FutureTask.get()方法或FutureTask.cancel(…)方法。除此以外,还可以单独使用FutureTask。
当一个线程需要等待另一个线程把某个任务执行完后它才能继续执行,此时可以使用FutureTask。假设有多个线程执行若干任务,每个任务最多只能被执行一次。当多个线程试图同时执行同一个任务时,只允许一个线程执行任务,其他线程需要等待这个任务执行完后才能继续执行。下面是对应的示例代码。
 

private final ConcurrentMap<Object, Future<String>> taskCache = new ConcurrentHashMap<>();

    private String executionTask(final String taskName)throws ExecutionException, InterruptedException {
        while (true) {
            Future<String> future = taskCache.get(taskName); // 1.1,2.1
            if (future == null) {
                Callable<String> task = () -> taskName;
                FutureTask<String> futureTask = new FutureTask<>(task);
                future = taskCache.putIfAbsent(taskName, futureTask); // 1.3
                if (future == null) {
                    future = futureTask;
                    futureTask.run(); // 1.4执行任务
                }
            }
            try {
                return future.get(); // 1.5,
            } catch (CancellationException e) {
                taskCache.remove(taskName, future);
            }
        }
    }

当两个线程试图同时执行同一个任务时,如果Thread 1执行1.3后Thread 2执行2.1,那么接下来Thread 2将在2.2等待,直到Thread 1执行完1.4后Thread 2才能从2.2(FutureTask.get())返回

FutureTask实现
jdk1.8的FutureTask有个说明:
修订说明:这与这个类以前依赖AbstractQueuedsynchronizer的版本不同,主要是为了避免在取消竞争期间保留中断状态让用户感到意外。在当前的设计中,Sync控件依赖于通过CAS更新的“state”字段来跟踪完成,以及一个简单的Treiber堆栈来保存等待的线程。
风格注意:与往常一样,我们绕过了使用 AtomicXFieldUpdaters的开销,而是直接使用Unsafe

Future
FutureTask实现了Future接口,Future接口有5个方法
1、boolean cancel(boolean mayInterruptIfRunning)
尝试取消当前任务的执行。如果任务已经取消、已经完成或者其他原因不能取消,尝试将失败。如果任务还没有启动就调用了cancel(true),任务将永远不会被执行。如果任务已经启动,参数mayInterruptIfRunning将决定任务是否应该中断执行该任务的线程,以尝试中断该任务。
如果任务不能被取消,通常是因为它已经正常完成,此时返回false,否则返回true
2、boolean isCancelled()
如果任务在正常结束之前被被取消返回true
3、boolean isDone()
正常结束、异常或者被取消导致任务完成,将返回true
4、V get()
等待任务结束,然后获取结果,如果任务在等待过程中被终端将抛出InterruptedException,如果任务被取消将抛出CancellationException,如果任务中执行过程中发生异常将抛出ExecutionException。
5、V get(long timeout, TimeUnit unit)
任务最多在给定时间内完成并返回结果,如果没有在给定时间内完成任务将抛出TimeoutException。

FutureTask状态转换

FutureTask有以下7中状态:

FutureTask任务的运行状态,最初为NEW。运行状态仅在setsetExceptioncancel方法中转换为终端状态。在完成过程中,状态可能呈现出瞬时值INTERRUPTING(仅在中断运行程序以满足cancel(true)的情况下)或者COMPLETING(在设置结果时)状态时。从这些中间状态到最终状态的转换使用成本更低有序/延迟写,因为值是统一的,需要进一步修改。
state:表示当前任务的运行状态,FutureTask的所有方法都是围绕state开展的,state声明为volatile,保证了state的可见性,当对state进行修改时所有的线程都会看到。


NEW:表示一个新的任务,初始状态
COMPLETING:当任务被设置结果时,处于COMPLETING状态,这是一个中间状态
NORMAL:表示任务正常结束。
EXCEPTIONAL:表示任务因异常而结束
CANCELLED:任务还未执行之前就调用了cancel(true)方法,任务处于CANCELLED
INTERRUPTING:当任务调用cancel(true)中断程序时,任务处于INTERRUPTING状态,这是一个中间状态
INTERRUPTED:任务调用cancel(true)中断程序时会调用interrupt()方法中断线程运行,任务状态由INTERRUPTING转变为INTERRUPTED

可能的状态过渡
 1、NEW -> COMPLETING -> NORMAL:正常结束
 2、NEW -> COMPLETING -> EXCEPTIONAL:异常结束
 3、NEW -> CANCELLED:任务被取消
 4、NEW -> INTERRUPTING -> INTERRUPTED:任务出现中断
 

知识来源:

FutureTask详解_索码理的博客-CSDN博客

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

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

相关文章

APP运营的核心是什么?

APP的运营的核心就是用户运营&#xff0c;主要分为四个指标&#xff1a;用户拉新、留存、促活、转化&#xff08;营收&#xff09;下面进行一一分析。 一、APP用户拉新&#xff1a;提高用户精准度 用户拉新&#xff0c;无非是把APP推广出去进行品牌曝光&#xff0c;提高下载量…

将 ordinals 与 比特币智能合约集成 : 第 1 部分

将序数与比特币智能合约集成&#xff1a;第 1 部分 最近&#xff0c;比特币序数在区块链领域引起了广泛关注。 据称&#xff0c;与以太坊 ERC-721 等其他代币标准相比&#xff0c;Ordinals 的一个主要缺点是缺乏对智能合约的支持。 我们展示了如何向 Ordinals 添加智能合约功…

CPU与GPU渲染的差异有哪些?最佳3D渲染 GPU推荐

什么是 GPU 渲染&#xff1f; GPU 渲染使您可以使用显卡而不是 CPU 进行渲染。从广义上讲&#xff0c;GPU渲染允许许多并行操作同时运行。这提高了执行速度&#xff0c;因为现代 GPU 旨在计算大量数据。快速渲染使 GPU 能够实时处理图形。但是&#xff0c;在这种情况下&#x…

前后端中的异步和事件机制 | 前后端开发

前言 在前后端程序设计开发工作中&#xff0c;小伙伴们一定都接触过事件、异步这些概念。出现这些概念的原因之一是&#xff0c;我们的代码在执行过程中所涉及的逻辑在不同的场合下执行时间的期望是各不相同的。为了尽量做到充分利用CPU等资源做尽可能多的事&#xff0c;免不了…

JWT解决跨域问题详解

介绍 在前后端不分离时&#xff0c;我们利用前面讲过的Spring Security的各种知识点&#xff0c;就可以实现对项目的权限管控。但是在前后端分离时&#xff0c;尤其是在引入了Spring Security后的前后端分离时&#xff0c;我们从前端发来的请求&#xff0c;就会存在一些问题。…

IBM Spectrum LSF Process Manager —— 设计、记录和运行复杂的计算工作流

亮点 ● 快速创建复杂的分布式工作流 ● 开发可重复的最佳实践 ● 自信地运行关键工作流程 ● 提高流程可靠性 IBM Spectrum LSF Process Manager 使您能够设计和自动化计算或分析流程&#xff0c; 捕获和保护可重复的最佳实践。 使用直观的图形界面…

时序预测 | MATLAB实现PSO-LSSVM粒子群算法优化最小二乘支持向量机时间序列预测未来

时序预测 | MATLAB实现PSO-LSSVM粒子群算法优化最小二乘支持向量机时间序列预测未来 目录 时序预测 | MATLAB实现PSO-LSSVM粒子群算法优化最小二乘支持向量机时间序列预测未来预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.Matlab实现PSO-LSSVM时间序列预测未…

Springboot 实践(13)spring boot 整合RabbitMq

前文讲解了RabbitMQ的下载和安装&#xff0c;此文讲解springboot整合RabbitMq实现消息的发送和消费。 1、创建web project项目&#xff0c;名称为“SpringbootAction-RabbitMQ” 2、修改pom.xml文件&#xff0c;添加amqp使用jar包 <!-- RabbitMQ --> <dependency&g…

如何确认this的值

<script>/*如何确认this的值1.全局执行环境严格模式&#xff0c;非严格模式&#xff1a;全局对象window2.函数内部环境2.1直接调用严格模式下&#xff1a;undefined非严格模式&#xff1a;全局对象&#xff08;window&#xff09;2.2对象方法调用严格模式&#xff0c;非严…

OCR扫描仪选购技巧,轻松将纸质文件转为电子文本

OCR&#xff08;光学字符识别&#xff09;扫描仪是一种特殊扫描仪&#xff0c;能够将纸质文件上的文字转换为可编辑的电子文本。在选购OCR扫描仪时&#xff0c;有一些技巧可以帮助我们选择到适合自己需求的产品。以下介绍几个简单实用的OCR扫描仪选购技巧。 首先&#xff0c;需…

使用pip下载第三方软件包报错超时处理方法

报错如下&#xff1a; WARNING: Retrying (Retry(total4, connectNone, readNone, redirectNone, statusNone)) after connection broken by ‘ReadTimeoutEr ror(“HTTPSConnectionPool(host‘files.pythonhosted.org’, port443): Read timed out. (read timeout15)”)’: /p…

【AGC】云数据库API9开发问题汇总

【问题描述】 云数据库HarmonyOS API9 SDK已经推出了一段时间了&#xff0c;下面为大家汇总一些在集成使用中遇到的问题和解决方案。 【问题分析】 1. 报错信息&#xff1a;数据库初始化失败&#xff1a;{“message”&#xff1a;“The object type list and permission …

开启Clash和系统代理后Chrome无法打开网页但Edge正常

今天早上打开电脑准备摸鱼&#xff0c;发现Chrome打不开网页了。检查Clash正常&#xff0c;切换了节点&#xff0c;依然不行。关闭系统的代理可以解决。不然只提示ERR_TIMED_OUT。 各种研究配置&#xff0c;然后发现Edge却又不受影响。 通过火绒发现Chrome是有连接到7890端口的…

【LeetCode】328. 奇偶链表

328. 奇偶链表&#xff08;中等&#xff09; 思路 如果链表为空&#xff0c;则直接返回链表。 对于原始链表&#xff0c;每个节点都是奇数节点或偶数节点。头节点是奇数节点&#xff0c;头节点的后一个节点是偶数节点&#xff0c;相邻节点的奇偶性不同。因此可以将奇数节点和偶…

Typora for Mac(Markdown文本编辑器)

Typora是一款简洁、直观的跨平台Markdown编辑器软件。它提供了一个非常直观和简单的界面&#xff0c;让用户可以更轻松地编写和编辑Markdown语言的文档。 Typora具有实时预览功能&#xff0c;这意味着你可以在编辑Markdown文档的同时立即看到最终的效果。它允许用户快速切换编辑…

简化车辆登记流程:利用腾讯云OCR实现自动化信息识别

项目中有一块&#xff0c;需要用到上传车牌车牌号到系统里&#xff0c;用了下腾讯云的ocr车牌号识别做了个小功能。通过腾讯云的orc识别&#xff0c;将车牌号录入到后台。 一&#xff0c;首先我们需要登录到腾讯云&#xff0c;然后搜索一下orc或者车牌号 https://curl.qcloud.…

一阶多智能体的平均一致性

数学表达 一阶多智能体的运动学方程可以描述为 x ˙ i ( t ) u i ( t ) , i ∈ { 1 , 2 , 3 , … , N } \dot x_i(t) u_i(t),i\in\{1,2,3,\dots,N\} x˙i​(t)ui​(t),i∈{1,2,3,…,N} 其中 x i ( t ) x_i(t) xi​(t)为状态&#xff0c; u i ( t ) u_i(t) ui​(t)为控制量&…

SIP mini 对讲终端,带sip热点功能

SV-A10/SV-A10W SIP mini 对讲终端&#xff0c;带sip热点功能 SV-A10/SV-A10W 是专门针对行业用户需求研发的一款 SIP mini 对讲产品&#xff0c;外观小巧&#xff0c;功能 强大&#xff0c;集智能安防、音/视频对讲和广播功能于一体&#xff0c;性价比高。支持壁挂式安装/86…

lv3 嵌入式开发-4 linux shell命令(文件搜索、文件处理、压缩)

目录 1 查看文件相关命令 1.1 常用命令 1.2 硬链接和软链接 2 文件搜索相关命令 2.1 查找文件命令 2.2 查找文件内容命令 2.3 其他相关命令 3 文件处理相关命令 3.1 cut 3.2 sed 过滤 3.3 awk 匹配 4 解压缩相关命令 4.1 解压缩文件的意义 4.2 解压缩相关命令 1 …

C++ | 负数比0大?

C | 负数比0大&#xff1f; 文章目录 C | 负数比0大&#xff1f;现象分析剖析赋值运算类型提升标准有符号整数类型的转换级别关系为&#xff1a;char short转换int long double 转换 >>>>> 欢迎关注公众号【三戒纪元】 <<<<< 现象 在 for 的判断…