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

JAVA并发编程系列(13)Future、FutureTask异步小王子

美团本地生活面试:模拟外卖订单处理,客户支付提交订单后,查询订单详情,后台需要查询店铺备餐进度、以及外卖员目前位置信息后再返回。

时间好快,一转眼不到一个月时间,已经完成分享synchronized、volatile、CAS、AQS、ReentrantLock、Semaphore、CountDownLatch、CyclicBarrier、并发锁、Condition、线程池、ThreadLocal等多个核心基础原理和案例剖析。

其实我编写文章速度真的很慢,基本每篇文章需要写2~3个小时去梳理。确保基础理论、源码分析、面试案例、优缺点等均分享到位,力求每篇都是干货实用,让每位看到我文章的同学,不管是面试、还是应用到工作实践,都能有所收益。

今天我们围绕Future是什么、怎么用,实践demo来展开分享,实现原理架构来展开。

一、Future是什么

首先我们回到一个问题,就是为什么需要Future、FutureTask?

之前我们用过的线程池ThreadPoolExecutor、线程Thread都可以执行异步任务,但是无法执行带有返回值的异步任务。而Future是可以执行这种带有返回值的异步任务。线程池ThreadPoolExecutor、Thread线程可以通过提交执行Future类型的任务,就可以获取任务返回值。

和Callable、Runnable一样,Future是一个接口。我们看一下它的接口源码。

//Runnable接口,只有一个run方法
@FunctionalInterface
public interface Runnable {public abstract void run();
}
//Callable接口,只有一个call方法
@FunctionalInterface
public interface Callable<V> {V call() throws Exception;
}

//本文主角Future有5个方法
public interface Future<V> {boolean cancel(boolean mayInterruptIfRunning);boolean isCancelled();boolean isDone();V get() throws InterruptedException, ExecutionException;V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;
}

代码非常少,有5个方法,但是核心方法是get(),还是isDone();

get()方法:用来读取任务的返回结果。此外,如果任务未执行,调用该方法的当前线程会进入阻塞等待。

isDone()方法:检查计算是否已完成。这个方法不会阻塞线程。日常使用Future,一般是先调用isDone()方法判断结果是否返回,然后再调用get()方法获取执行结果。

一句话总结:Future是异步计算任务,提交Future任务后可以继续干别的;等干完别的事,再回来通过get()方法去读取Future任务结果

再简单总结:Future是支持在未来读取结果的异步计算任务。

cancel()方法:用来尝试取消任务,仅仅是尝试,不一定成功。如果任务已经开始执行,那么它就不能被取消。

isCancelled()方法:就是用来检查这个Future任务是否被取消。

Future和之前分享的信号量Semaphore、CountDownLatch倒数门闩、CyclicBarrier循环屏障都不一样,唯一和Condition条件队列有点像,支持多个线程协调进行。支持异步读取任务结果这个特性,Future可以很方便支持多个任务并发执行,以及在最后汇总获取并发结果,最后返回给终端。

二、应用实践:模拟同时查外卖信息

我们用FutureTask实现一个并发面试题:模拟外卖订单处理,客户支付提交订单后,查询订单详情,后台需要查询店铺备餐进度、以及外卖员目前位置信息后再返回。

package lading.java.mutithread;import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;/*** 模拟外卖订单处理,客户支付提交订单后,查询订单详情,后台* 通过查询店铺备餐进度、以及外卖员目前位置信息。*/
public class Demo015Future {public static void main(String[] args) throws ExecutionException, InterruptedException {//1、异步查询商家系统任务FutureTask<Boolean> checkFoodIsReadyTask = new FutureTask<>(() -> {System.out.println(Thread.currentThread().getName()+"转发请求到商家系统,查询餐厅当前订单备餐进度...");Thread.sleep(2000);boolean foodIsOk = true;System.out.println(Thread.currentThread().getName()+"商家接口返回是否备餐完成,结果是:" + foodIsOk);return foodIsOk;});//2、异步查询外卖员系统任务FutureTask<Boolean> checkCourierIsReadyTask = new FutureTask<>(() -> {System.out.println(Thread.currentThread().getName()+"转发请求到外卖员系统,查询外卖员是否已到店...");Thread.sleep(800);boolean courierIsOk = true;System.out.println(Thread.currentThread().getName()+"查询外卖员是否已到店,结果是:" + courierIsOk);return courierIsOk;});//并发查询商家、外卖员情况,大约2snew Thread(checkFoodIsReadyTask).start();new Thread(checkCourierIsReadyTask).start();Thread.sleep(100);//只是判断是否完成,不会阻塞if (!checkFoodIsReadyTask.isDone()) {System.out.println(Thread.currentThread().getName() + "查询店铺备餐进度未完成,继续等等...");}if (!checkCourierIsReadyTask.isDone()) {System.out.println(Thread.currentThread().getName() + "查询外卖员情况未完成,继续等等...");}//如果结果没返回会阻塞等待System.out.println(Thread.currentThread().getName() + "线程 成功查到商家备餐结果:" + checkFoodIsReadyTask.get());System.out.println(Thread.currentThread().getName() + "线程 成功查到外卖员结果:" + checkCourierIsReadyTask.get());}
}

运行结果:

三、硬核干活:FutureTask的实现原理源码分析

我们从它的属性开始,然后讲实现的方法原理。

3.1 FutureTask的属性

FutureTask 的源码也不多,属性就这5个。

线程状态state、执行任务的callable,任务执行的返回结果outCome,正在运行的线程runner,等待队列里的waiters节点。

3.1.1 当前任务线程状态state,以及状态枚举

这七种任务状态之间相互转换关系:

1、正常结束

NEW -> COMPLETING -> NORMAL

2、异常结束

NEW -> COMPLETING -> EXCEPTIONAL

3、任务被取消

NEW -> CANCELLED

4、任务出现中断

NEW -> INTERRUPTING -> INTERRUPTED

//当前任务线程的状态,以下几个枚举值都是state不同状态
private volatile int state;//当前任务线程刚创建状态
private static final int NEW          = 0;
//当前任务线程即将完成,ing是一个即将完成状态
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;
3.2 其他属性意义
//Callable 来执行任务,Callable 有返回值的线程
private Callable<V> callable;//Callable任务执行的返回结果
private Object outcome; //当前正在运行的线程
private volatile Thread runner;//等待队列节点,WaitNode是FutureTask的内部类
private volatile WaitNode waiters;

waiters等待队列:这个是单向队列,里面是等待本任务执行结果的线程。比如ABCD四个线程,其中A线程执行了任务,B、C、D线程,都等A线程的返回结果,就需要在waiters等待队列里等着。

//等待队列源码static final class WaitNode {//被阻塞的线程,也就volatile Thread thread;//这里看出它就是个单向队列volatile WaitNode next;WaitNode() { thread = Thread.currentThread(); }}
3.3 run()方法原理

run方法比较简单,不放源码,直接说,就是执行定义的callable任务,任务执行完成后,通过CAS去更新outCome返回值。

    protected void set(V v) {if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {outcome = v;UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final statefinishCompletion();}}

3.4 get()方法原理

这个方法和run()一样重要,其他线程想要获取本任务结果,都是通过get方法读取。逻辑也是很简单。其中waitDone()方法,就是进入等待等列等结果。

    public V get() throws InterruptedException, ExecutionException {int s = state;//1、如果任务没执行完成,当前想读取任务结果的线程就进入阻塞等待if (s <= COMPLETING)s = awaitDone(false, 0L);//2、如果任务执行完成,就返回结果    return report(s);}
3.5 其他方法
    //当前任务状态是否被取消,直接读状态值public boolean isCancelled() {return state >= CANCELLED;}//当前任务状态是否以及执行结束,直接读状态值public boolean isDone() {return state != NEW;}

本系列文章推荐:

1、JAVA并发编程系列(12)ThreadLocal就是这么简单|建议收藏

2、JAVA并发编程系列(11)线程池底层原理架构剖析

相关文章:

JAVA并发编程系列(13)Future、FutureTask异步小王子

美团本地生活面试&#xff1a;模拟外卖订单处理&#xff0c;客户支付提交订单后&#xff0c;查询订单详情&#xff0c;后台需要查询店铺备餐进度、以及外卖员目前位置信息后再返回。 时间好快&#xff0c;一转眼不到一个月时间&#xff0c;已经完成分享synchronized、volatile、…...

【python爬虫可以获取到谷歌影像吗?】如何有效下载谷歌影像?

【python爬虫可以获取到谷歌影像吗&#xff1f;】如何有效下载谷歌影像&#xff1f; 【python爬虫可以获取到谷歌影像吗&#xff1f;】如何有效下载谷歌影像&#xff1f; 文章目录 【python爬虫可以获取到谷歌影像吗&#xff1f;】如何有效下载谷歌影像&#xff1f;前言1. 使用…...

Windows 上安装 PostgreSQL

Windows 上安装 PostgreSQL PostgreSQL 是一款功能强大的开源关系数据库管理系统,广泛用于各种应用场景。在 Windows 系统上安装 PostgreSQL 相对简单,但需要遵循一系列步骤。本文将详细介绍在 Windows 上安装 PostgreSQL 的过程,并提供一些关键的配置和优化建议。 一、下…...

Vue 技术进阶 day2 数据监视的原理、其他内置指令、自定义指令、生命周期、组件化、VueComponent构造函数

目录 1.Vue监测数据的原理 1.1 原理 1.1.1 数据劫持 1.1.2 观察者模式(Vue内部的实现) 1.1.3 更新组件 1.1.4 计算属性和侦听器 1.2 后添加属性做响应式&#xff08;Vue.set / vm.$set&#xff09; 1.3 对象和数组的响应式 1.4 数据监视案例 2.指令 2.1 内置指令 2.…...

vue.js 原生js app端实现图片旋转、放大、缩小、拖拽

效果图&#xff1a; 旋转 放大&#xff1a;手机上可以双指放大缩小 拖拽 代码实现&#xff1a; html <div id"home" class"" v-cloak><!-- 上面三个按钮 图片自己解决 --><div class"headImage" v-if"showBtn">&l…...

MyBatis的注入问题

对之前文章的补充&#xff1a;MyBatis中的#{}与${}注入问题----原文链接 前言&#xff1a; MyBatis是一个流行的Java持久层框架&#xff0c;用于将对象与数据库中的数据进行映射。然而&#xff0c;如果不当使用&#xff0c;MyBatis也可能受到诸如SQL注入这类的安全问题的影响。…...

基于springboot的评分评教管理系统

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于springboot的评分评教管理系统1拥有三种角色 管理员&#xff1a;评价管理、学生管理、评分指标管理、课程管理、教师管理、管理员管理等教师&#xff1a;课程管理、学生管理、个人信…...

C嘎嘎入门篇:类和对象(2)

前言&#xff1a; 上一篇小编讲了类和对象&#xff08;1&#xff09;&#xff0c;当然&#xff0c;在看这篇文章之前&#xff0c;读者朋友们一定要掌握好前面的基础内容&#xff0c;因为这篇和前面息息相关&#xff0c;废话不多说&#xff0c;下面小编就加快步伐&#xff0c;开…...

数据库 - Mongo数据库

目录 前言 一、MongoDB的特点 二、Mongo的核心概念 三、MongoDB的优劣势 四、使用场景 五、MongoDB与其他数据库的对比 六、如何安装MongoDB 七、数据库指令操作 &#xff08;一&#xff09;基本数据库操作 &#xff08;1&#xff09;连接 MongoDB &#xff08;2&am…...

工业控制过等保三级需要的网络安全设备及详细讲解

在工业控制系统&#xff08;ICS&#xff09;的安全性日益受到重视的背景下&#xff0c;网络安全等级保护&#xff08;过等保&#xff09;三级作为一种重要的安全标准&#xff0c;对保障工业控制系统的安全运行有着重要的意义。过等保三级主要针对那些对安全性要求较高的系统&am…...

Android开发高级篇:MVVM框架与数据双向绑定

在Android开发中&#xff0c;MVVM&#xff08;Model-View-ViewModel&#xff09;架构模式以其高效、简洁的特点&#xff0c;成为越来越多开发者的首选。MVVM不仅实现了界面&#xff08;UI&#xff09;与业务逻辑的分离&#xff0c;还通过数据双向绑定技术&#xff0c;极大地简化…...

智能招聘系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;企业管理&#xff0c;招聘信息管理&#xff0c;应聘信息管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;招聘信息&#xff0c;我的 开发系统&…...

Wireshark抓包GRPC协议查看Protobuf编码内容

1.说明 对通过GRPC协议进行通信的流量进行抓包后&#xff0c; 需要先转换为HTTP2协议&#xff0c; 因为默认解析的HTTP协议和TCP协议无法进行后续的查看操作&#xff0c; 然后再通过加载protobuf文件&#xff0c; 对GRPC内的DATA字段进行解码。 2.抓包 本文为了测试方便&…...

selenium 强制、隐式、显示等待(11种预置条件)

注&#xff1a;显示等待和隐式等待不可混用 强制等待 让当前正在执行的代码线程暂停运行。 示例&#xff1a;在电商网站的商品搜索页面&#xff0c;等待 5 秒之后&#xff0c;点击搜索按钮&#xff0c;如果页面加载速度很快&#xff0c;在 2 秒内生成&#xff0c;那么还需要…...

ffmpeg拉取rtsp网络视频流报错解析

在使用ffmpeg调用api方式对一个rtsp网络视频流拉流播放时&#xff0c;应用程序出现了一些错误提示&#xff0c;并且拉流播放的画面也出现了一些马赛克的现象。所以这里便对应用程序所产生的错误提示进行了详细的研究和分析。这里将分析结果贴在下面&#xff0c;若其他朋友遇到类…...

c# iTextSharp 读取PDF

安装 iTextSharp&#xff1a; 可以通过 NuGet 包管理器安装 iTextSharp&#xff1a; Install-Package itext7创建 PDF 文件&#xff1a; using System; using System.IO; using iText.Kernel.Pdf; using iText.Layout; using iText.Layout.Element;class Program {static voi…...

<<迷雾>> 第5章 从逻辑学到逻辑电路(3)--与门 示例电路

与门及其符号&#xff08;2输入端&#xff09; info::操作说明 鼠标单击开关切换开合状态 系统中使用 半方形半圆形 表示与门 primary::在线交互操作链接 https://cc.xiaogd.net/?startCircuitLinkhttps://book.xiaogd.net/cyjsjdmw-examples/assets/circuit/cyjsjdmw-ch05-11…...

Java应用的数据库连接池连接超时处理

Java应用的数据库连接池连接超时处理 大家好&#xff0c;我是微赚淘客返利系统3.0的小编&#xff0c;是个冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在Java应用中&#xff0c;数据库连接池是管理数据库连接的重要组件。然而&#xff0c;当数据库负载过高或网…...

机器学习:opencv--摄像头OCR

目录 前言 一、三个函数 1.显示图像 2.点排序 3.透视变换 二、代码实例 1.打开摄像头 2.图像预处理 3.检测特定轮廓 4.对轮廓进行处理 5.释放资源 前言 摄像头OCR指的是利用摄像头捕捉图像中的文字信息&#xff0c;并通过光学字符识别&#xff08;OCR&#xff09;技…...

基于二分查找的动态规划 leetcode 300.最长递增子序列

如题&#xff1a; https://leetcode.cn/problems/longest-increasing-subsequence/description/ 其实常规动态规划的解法就没什么好说的了&#xff0c;有意思的是官方放出了一个二分查找的动态规化解法&#xff0c;时间复杂度能降到O(nlog(n))&#xff0c;但是为什么这样能解&…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构

React 实战项目&#xff1a;微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇&#xff01;在前 29 篇文章中&#xff0c;我们从 React 的基础概念逐步深入到高级技巧&#xff0c;涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...

车载诊断架构 --- ZEVonUDS(J1979-3)简介第一篇

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…...

使用python进行图像处理—图像滤波(5)

图像滤波是图像处理中最基本和最重要的操作之一。它的目的是在空间域上修改图像的像素值&#xff0c;以达到平滑&#xff08;去噪&#xff09;、锐化、边缘检测等效果。滤波通常通过卷积操作实现。 5.1卷积(Convolution)原理 卷积是滤波的核心。它是一种数学运算&#xff0c;…...

可视化预警系统:如何实现生产风险的实时监控?

在生产环境中&#xff0c;风险无处不在&#xff0c;而传统的监控方式往往只能事后补救&#xff0c;难以做到提前预警。但如今&#xff0c;可视化预警系统正在改变这一切&#xff01;它能够实时收集和分析生产数据&#xff0c;通过直观的图表和警报&#xff0c;让管理者第一时间…...

codeforces C. Cool Partition

目录 题目简述&#xff1a; 思路&#xff1a; 总代码&#xff1a; https://codeforces.com/contest/2117/problem/C 题目简述&#xff1a; 给定一个整数数组&#xff0c;现要求你对数组进行分割&#xff0c;但需满足条件&#xff1a;前一个子数组中的值必须在后一个子数组中…...

LeetCode第244题_最短单词距离II

LeetCode第244题&#xff1a;最短单词距离II 问题描述 设计一个类&#xff0c;接收一个单词数组 wordsDict&#xff0c;并实现一个方法&#xff0c;该方法能够计算两个不同单词在该数组中出现位置的最短距离。 你需要实现一个 WordDistance 类: WordDistance(String[] word…...

[electron]预脚本不显示内联script

script-src self 是 Content Security Policy (CSP) 中的一个指令&#xff0c;它的作用是限制加载和执行 JavaScript 脚本的来源。 具体来说&#xff1a; self 表示 当前源。也就是说&#xff0c;只有来自当前网站或者当前页面所在域名的 JavaScript 脚本才被允许执行。"…...