FutureTask简介

news2024/11/27 20:32:01

FutureTask简介

Future接口和实现Future接口的FutureTask类,代表异步计算的结果
FutureTask除了实现Future接口外,还实现了Runnable接口。因此,FutureTask可以交给Executor执行,也可以由调用线程直接执行(FutureTask.run())
根据FutureTask.run()方法被执行的时机,FutureTask可以处于下面3种状态。
1)未启动。FutureTask.run()方法还没有被执行之前,FutureTask处于未启动状态。当创建一个FutureTask,且没有执行FutureTask.run()方法之前,这个FutureTask处于未启动状态。
2)已启动。FutureTask.run()方法被执行的过程中,FutureTask处于已启动状态。
3)已完成。FutureTask.run()方法执行完后正常结束,或被取消(FutureTask.cancel(…)),或执行FutureTask.run()方法时抛出异常而异常结束,FutureTask处于已完成状态。
下图是FutureTask的状态迁移的示意图。

在这里插入图片描述

FutureTask的状态迁移示意图解释
当FutureTask处于未启动或已启动状态时,执行FutureTask.get()方法将导致调用线程阻塞
当FutureTask处于已完成状态时,执行FutureTask.get()方法将导致调用线程立即返回结果抛出异常
当FutureTask处于未启动状态时,执行FutureTask.cancel()方法将导致此任务永远不会被执行
当FutureTask处于已启动状态时,执行FutureTask.cancel(true)方法将以中断执行此任务线程的方式来试图停止任务;
当FutureTask处于已启动状态时,执行FutureTask.cancel(false)方法将不会对正在执行此任务的线程产生影响(让正在执行的任务运行完成);
当FutureTask处于已完成状态时,执行FutureTask.cancel(…)方法将返回false
下图是get方法和cancel方法的执行示意图。

在这里插入图片描述

FutureTask的使用

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

                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,2.2线程在此等待任务执行完成
            } 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的实现

FutureTask的实现基于AbstractQueuedSynchronizer(以下简称为AQS)。
AQS 是一个同步框架,它提供通用机制来原子性管理同步状态、阻塞和唤醒线程,以及维护被阻塞线程的队列。JDK 6中AQS被广泛使用,基于AQS实现的同步器包括:ReentrantLock、Semaphore、ReentrantReadWriteLock、CountDownLatch和FutureTask。
每一个基于AQS实现的同步器都会包含两种类型的操作,如下。
·至少一个acquire操作。这个操作阻塞调用线程,除非/直到AQS的状态允许这个线程继续执行。FutureTask的acquire操作为get()/get(long timeout,TimeUnit unit)方法调用。
·至少一个release操作。这个操作改变AQS的状态,改变后的状态可允许一个或多个阻塞线程被解除阻塞。FutureTask的release操作包括run()方法和cancel(…)方法。
基于“复合优先于继承”的原则,FutureTask声明了一个内部私有的继承于AQS的子类Sync,对FutureTask所有公有方法的调用都会委托给这个内部子类。
AQS被作为“模板方法模式”的基础类提供给FutureTask的内部子类Sync,这个内部子类只需要实现状态检查状态更新的方法即可,这些方法将控制FutureTask的获取和释放操作。具体来说,Sync实现了AQS的tryAcquireShared(int)方法和tryReleaseShared(int)方法,Sync通过这两个方法来检查和更新同步状态。
FutureTask的设计示意图如下图所示

在这里插入图片描述

FutureTask.get() 方法会调用AQS.acquireSharedInterruptibly(int arg)方法,这个方法的执行过程如下。
1)调用AQS.acquireSharedInterruptibly(int arg)方法,这个方法首先会回调在子类Sync中实现的tryAcquireShared()方法来判断acquire操作是否可以成功。acquire操作可以成功的条件为:state为执行完成状态RAN或已取消状态CANCELLED,且runner不为null
2)如果成功则get()方法立即返回。如果失败则到线程等待队列中去等待其他线程执行release操作。
3)当其他线程执行release操作(比如FutureTask.run()FutureTask.cancel(…))唤醒当前线程后,当前线程再次执行tryAcquireShared()将返回正值1,当前线程将离开线程等待队列并唤醒它的后继线程(这里会产生级联唤醒的效果,后面会介绍)。
4)最后返回计算的结果或抛出异常。
FutureTask.run() 的执行过程如下。
1)执行在构造函数中指定的任务(Callable.call())。
2)以原子方式来更新同步状态(调用AQS.compareAndSetState(int expect,int update),设置state为执行完成状态RAN)。如果这个原子操作成功,就设置代表计算结果的变量result的值为Callable.call()的返回值,然后调用AQS.releaseShared(int arg)。
3)AQS.releaseShared(int arg)首先会回调在子类Sync中实现的tryReleaseShared(arg)来执行release操作(设置运行任务的线程runner为null,然会返回true);AQS.releaseShared(int arg),然后唤醒线程等待队列中的第一个线程。
4)调用FutureTask.done()。
当执行FutureTask.get()方法时,如果FutureTask不是处于执行完成状态RAN或已取消状态CANCELLED,当前执行线程将到AQS的线程等待队列中等待(见下图的线程A、B、C和D)。当某个线程执行FutureTask.run()方法或FutureTask.cancel(…)方法时,会唤醒线程等待队列的第一个线程(见下图所示的线程E唤醒线程A)。

在这里插入图片描述

针对上图FutureTask的级联唤醒示意图
假设开始时FutureTask处于未启动状态或已启动状态,等待队列中已经有3个线程(A、B和C)在等待。此时,线程D执行get()方法将导致线程D也到等待队列中去等待。
当线程E执行run()方法时,会唤醒队列中的第一个线程A。线程A被唤醒后,首先把自己从队列中删除,然后唤醒它的后继线程B,最后线程A从get()方法返回。线程B、C和D重复A线程的处理流程。最终,在队列中等待的所有线程都被级联唤醒并从get()方法返回。

-----------------------------------------------------------------------------读书笔记摘自 书名:Java并发编程的艺术 作者:方腾飞;魏鹏;程晓明

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

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

相关文章

华为OD机试真题 Java 实现【矩阵最大值】【2023 B卷 100分】,附详细解题思路

一、题目描述 给定一个仅包含0和1的N*N的二维矩阵&#xff0c;请计算二维矩阵的最大值。 计算规则如下&#xff1a; 1、每行元素按下标顺序组成一个二进制数&#xff08;下标越大越排在低位&#xff09;&#xff0c;二进制数的值就是该行的值。矩阵各行值之和为矩阵的值。 …

使用Windbg动态调试目标进程的一般步骤及相关要点详解

目录 1、概述 2、将Windbg附加到已经启动起来的目标进程上&#xff0c;或者用Windbg启动目标程序 2.1、将Windbg附加到已经启动起来的目标进程上 2.2、用Windbg启动目标程序 2.3、Windbg关联到目标进程上会中断下来&#xff0c;输入g命令将该中断跳过去 3、分析实例说明 …

ModuleNotFoundError: No module named ‘transformers_modules.chatglm-6b_v1‘的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

ggplot2、RMySQL、httpuv、shiny、miniUI、devtools、recharts安装问题

目录 ggplot2下载成功&#xff01; RMySQL下载成功&#xff01; automake-1.16.tar.gz下载成功&#xff01; httpuv下载成功&#xff01; shiny下载成功&#xff01; miniUI下载成功&#xff01; devtools下载成功&#xff01; recharts下载成功&#xff01; 首先的首先…

shiro 550 反序列化rce

Apach shiro 是一款开源安全框架&#xff0c;提供身份验证&#xff0c;授权&#xff0c;会话管理等。 shiro 550 反序列化漏洞rce 通关利用它反序列化的漏洞直接执行rce 加密的用户信息序列化后储存在名为remenber -me的cooike中。攻击者可以使用shiro默认密钥伪造cooike&am…

django连接mysql一些报错解决方法

1.AttributeError: str object has no attribute ‘decode’ 2.django.db.utils.OperationalError: (2003, "Can’t connect to MySQL server on ‘localhost’ ([WinError 10] 解决方法&#xff1a;仔细核对#数据库引擎和#数据库的主机地址 DATABASES { ‘default’:…

2023PS beta 官方注册安装教程

该教程为官方注册下载教程&#xff0c;无风险。 软件介绍 Adobe Photoshop 2023版(简称PS)是一款全球流行的专业图像处理软件及照片和设计软件。Adobe Photoshop中文版是Adobe Creative Cloud 创意云桌面程序中心的图形设计软件热门产品&#xff0c;它是平面设计领域和数字图象…

读改变未来的九大算法笔记03_纠错码

1. 真正根源 1.1. 在电报和电话等通信系统中出现的 1.2. 理查德汉明创造了第一批纠错码&#xff1a;一种近乎神奇的能侦测并纠正计算机数据中错误的算法 2. 信息理论学的一部分 2.1. Information Theory 2.2. 香农通过数学展示了有可能从根本上通过一个嘈杂的、引发错误的…

每日学术速递6.1

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.LayoutGPT: Compositional Visual Planning and Generation with Large Language Models 标题&#xff1a;LayoutGPT&#xff1a;具有大型语言模型的组合视觉规划和生成 作者&…

Linux开发工具:gcc和g++

目录 一. 什么是gcc和g 二. gcc的基本使用方法 三. 库和链接 3.1 动态库和静态库 3.2 动态链接和静态链接 四. Debug和Release 五. makefile和make 六. 总结 一. 什么是gcc和g gcc&#xff1a;Linux下编译C语言程序的编译器g&#xff1a;Linux下编译C代码的编译器 由…

Apache网页的优化与安全

文章目录 Apache 网页的压缩Apache的页面缓存Apache页面隐藏版本信息Apache页面设置防盗链 Apache 网页的压缩 检查压缩模块 apachectl -t -D DUMP_MODULES | grep "deflate"安装mod_deflate 模块 如果没有安装mod_deflate 模块&#xff0c;重新编译安装 Apache 添…

嵌入式STM32中时钟系统详细分析

1. STM32的时钟源主要有&#xff1a; 内部时钟 外部时钟 锁相环倍频输出时钟 1.1 详细介绍 HSI(内部高速时钟) 它是RC振荡器&#xff0c;频率可以达到8MHZ&#xff0c;可作为系统时钟和PLL锁相环的输入。 HSE&#xff08;外部高速时钟&#xff09; 接入晶振范围是4-16MHZ…

深入理解设计原则之组件构建原则【软件架构设计】

系列文章目录 C高性能优化编程系列 深入理解软件架构设计系列 深入理解设计模式系列 高级C并发线程编程 组件构建原则 系列文章目录1、组件构建原则的定义和解读1、组件2、组件聚合2.1、复用/发布等同原则&#xff08;REP&#xff09;2.2 、共同闭包原则&#xff08;CCP&…

C++(6):函数

函数基础 典型的函数包括&#xff1a;返回类型、函数名字、由 0 个或多个形参组成的列表以及函数体。 通过调用运算符&#xff08;call operator&#xff09;来执行函数。 调用运算符的形式是一对圆括号&#xff0c;它作用于一个表达式&#xff0c;该表达式是函数或者指向函数…

1731_makefile编写小结1_编译同目录下的文件

全部学习汇总&#xff1a; GreyZhang/g_makefile: Learn makefile from all kinds of tutorials on the web. Happy hacking and lets find an common way so we may dont need to touch makefile code any more! (github.com) 欢迎路过的YUAN类朋友相互交流&#xff0c;以下是…

每日学术速递6.2

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CL 1.BiomedGPT: A Unified and Generalist Biomedical Generative Pre-trained Transformer for Vision, Language, and Multimodal Tasks 标题&#xff1a;BiomedGPT&#xff1a;用于…

chatgpt赋能python:Python反向99乘法表:简单易学的终极练习

Python反向99乘法表&#xff1a;简单易学的终极练习 Python是一门强大而又容易上手的编程语言&#xff0c;而反向99乘法表则是一个极佳的练手项目。不仅能锻炼Python的基本语法和逻辑思维&#xff0c;同时也能体现出代码的风格和美感。本文将以Python反向99乘法表为例&#xf…

基于matlab仿真L形金属块基于时间温度分布图

一、前言 此示例说明了如何使用 Simulink 3D 动画™和 MATLAB 接口来操作复杂对象。 在此示例中&#xff0c;矩阵类型的数据在 MATLAB 和虚拟现实世界之间传输。使用此功能&#xff0c;您可以实现大量的颜色变化或变形。这对于可视化各种物理过程很有用。 我们在L形金属块中使用…

Chain of Thought Prompting和Zero Shot Chain of Thought初步认识

1. 思维链提示&#xff08;Chain-of-Thought Prompting&#xff09; 思维链(Chain-of-Thought:CoT)提示过程是一种最近开发的提示方法&#xff0c;它鼓励大语言模型解释其推理过程。下图显示了 few shot standard prompt&#xff08;左)与链式思维提示过程&#xff08;右&…

ChatGPT提示词攻略之基本原则

下面是调用openai的completion接口的函数。但在本文中并不是重点。了解一下就好。 import openai import osfrom dotenv import load_dotenv, find_dotenv _ load_dotenv(find_dotenv())openai.api_key os.getenv(OPENAI_API_KEY)def get_completion(prompt, model"gp…