线程池CompletableFuture异步编排复习笔记

一、线程回顾

1.1 初始化线程的 4 种方式

1)、继承 Thread

public static class Thread01 extends Thread {@Overridepublic void run() {System.out.println("当前线程:" + Thread.currentThread().getId());int i = 10 / 2;System.out.println("运行结果:" + i);}}

2)、实现 Runnable 接口

public static class Runable01 implements Runnable {@Overridepublic void run() {System.out.println("当前线程:" + Thread.currentThread().getId());int i = 10 / 2;System.out.println("运行结果:" + i);}}

3)、实现 Callable 接口 + FutureTask (可以拿到返回结果,可以处理异常)

public static class Callable01 implements Callable<Integer> {@Overridepublic Integer call() throws Exception {System.out.println("当前线程:" + Thread.currentThread().getId());int i = 10 / 2;System.out.println("运行结果:" + i);return i;}}
public static void main(String[] args) {FutureTask<Integer> futureTask = new FutureTask<>(new Callable01());new Thread(futureTask).start();System.out.println(futureTask.get());
}

4)、线程池

方式 1 和方式 2:主进程无法获取线程的运算结果。不适合当前场景
方式 3:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。可以导致服务器资源耗尽。
方式 4:通过如下两种方式初始化线程池

Executors.newFiexedThreadPool(3);
//或者
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit unit, workQueue, threadFactory, handler);

通过线程池性能稳定,也可以获取执行结果并捕获异常。但是,在业务复杂情况下,一个异步调用可能会依赖于另一个异步调用的执行结果

1.2 线程池的七大参数

@param **corePoolSize** the number of threads to keep in the pool, even if they are idle, unless {@code allowCoreThreadTimeOut} is set
池中一直保持的线程的数量,即使线程空闲。除非设置了 allowCoreThreadTimeOut@param **maximumPoolSize** the maximum number of threads to allow in the pool
池中允许的最大的线程数@param **keepAliveTime** when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating. 
当线程数大于核心线程数的时候,线程在最大多长时间没有接到新任务就会终止释放,
最终线程池维持在 corePoolSize 大小@param **unit** the time unit for the {@code keepAliveTime} argument
时间单位@param **workQueue** the queue to use for holding tasks before they are executed. This queue will hold only the {@code Runnable} tasks submitted by the {@code execute} method. 
阻塞队列,用来存储等待执行的任务,如果当前对线程的需求超过了 corePoolSize
大小,就会放在这里等待空闲线程执行。@param **threadFactory** the factory to use when the executor
creates a new thread
创建线程的工厂,比如指定线程名等@param **handler** the handler to use when execution is blocked
because the thread bounds and queue capacities are reached
拒绝策略,如果线程满了,线程池就会使用拒绝策略。

运行流程
1、线程池创建,准备好 core 数量的核心线程,准备接受任务
2、新的任务进来,用 core 准备好的空闲线程执行。
(1) 、core 满了,就将再进来的任务放入阻塞队列中。空闲的 core 就会自己去阻塞队列获取任务执行
(2) 、阻塞队列满了,就直接开新线程执行,最大只能开到 max 指定的数量
(3) 、max 都执行好了。Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自动销毁。最终保持到 core 大小
(4) 、如果线程数开到了 max 的数量,还有新任务进来,就会使用 reject 指定的拒绝策略进行处理
3、所有的线程创建都是由指定的 factory 创建的。

面试问题
一个线程池 core 7; max 20 ,queue:50,100 并发进来怎么分配的:
先有 7 个能直接得到执行,接下来 50 个进入队列排队,在多开 13 个线程继续执行。现在 70 个被安排上了。剩下 30 个默认拒绝策略。

1.3 常见的 4 种线程池

  • newCachedThreadPool
    core=0,创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  • newFixedThreadPool:
    core = max,创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  • newScheduledThreadPool
    创建一个定长线程池,支持定时及周期性任务执行。
  • newSingleThreadExecutor
    创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

1.4 开发中为什么使用线程池

  • 降低资源的消耗
    通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗。
  • 提高响应速度
    因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行
  • 提高线程的可管理性
    线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配

二、CompletableFuture 异步编排

业务场景
查询商品详情页的逻辑比较复杂,有些数据还需要远程调用,必然需要花费更多的时间。
在这里插入图片描述
假如商品详情页的每个查询,需要如下标注的时间才能完成,
那么,用户需要 5.5s 后才能看到商品详情页的内容。很显然是不能接受的。
如果有多个线程同时完成这 6 步操作,也许只需要 1.5s 即可完成响应。

2.0 初步认识

在 Java 8 中, 新增加了一个包含 50 个方法左右的类: CompletableFuture,提供了非常强大的Future 的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以
通过回调的方式处理计算结果,并且提供了转换和组合 CompletableFuture 的方法。CompletableFuture 类实现了 Future 接口,所以你还是可以像以前一样通过get方法阻塞或
者轮询的方式获得结果,但是这种方式不推荐使用。
CompletableFuture 和 FutureTask 同属于 Future 接口的实现类,都可以获取线程的执行结果。
在这里插入图片描述
注意(后续不再强调):CompletableFuture中方法不以 Async 结尾,意味着 Action 使用相同的线程执行,而 Async 可能会使用其他线程执行(如果是使用相同的线程池,也可能会被同一个线程选中执行)

2.1 创建异步对象

CompletableFuture 提供了四个静态方法来创建一个异步操作。
在这里插入图片描述
区别:
1、runXxxx 都是没有返回结果的,supplyXxx 都是可以获取返回结果的
2、可以传入自定义的线程池(推荐使用),否则就用默认的线程池;

2.2 计算完成时回调方法

在这里插入图片描述
whenComplete系列方法只能感知异常,不能修改CompletableFuture的返回值,若要返回异常信息,可以再链式调用exceptionally()方法,并指定一个出现异常以后默认的返回值。
whenComplete()需要传入参数 “消费者” BiConsumer<? super T, ? super Throwable> action,BiConsumer中accept(t,u)方法接收两个参数,第一个参数是future的返回结果,第二个参数是抛出的异常,没有返回值。
在这里插入图片描述

public class ThreadTest {public static ExecutorService executor = Executors.newFixedThreadPool(10);public static void main(String[] args) throws ExecutionException, InterruptedException {System.out.println("main......start.....");CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {System.out.println("当前线程:" + Thread.currentThread().getId());int i = 10 / 0;System.out.println("运行结果:" + i);return i;}, executor).whenComplete((res,exception) -> {//虽然能得到异常信息,但是没法修改返回数据System.out.println("异步任务成功完成了...结果是:" + res + "异常是:" + exception);}).exceptionally(throwable -> {//可以感知异常,同时返回默认值return 10;});System.out.println("main......end....." + future.get());}
}

执行结果:

main......start.....
当前线程:20
异步任务成功完成了...结果是:null异常是:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
main......end.....10

2.3 handle 方法

在这里插入图片描述
和 whenComplete 一样,可对结果做最后的处理(可处理异常),可改变返回值。
handle()方法需要传入参数 BiFunction<? super T, Throwable, ? extends U> fn,BiFunction中的R apply(T t, U u)方法接收两个参数T t 和 U u ,并且有返回值R
在这里插入图片描述

public class ThreadTest {public static ExecutorService executor = Executors.newFixedThreadPool(10);public static void main(String[] args) throws ExecutionException, InterruptedException {System.out.println("main......start.....");CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {System.out.println("当前线程:" + Thread.currentThread().getId());int i = 10 / 2;System.out.println("运行结果:" + i);return i;}, executor).handle((result,thr) -> {if (result != null) {return result * 2;}if (thr != null) {System.out.println("异步任务成功完成了...结果是:" + result + "异常是:" + thr);return 0;}return 0;});System.out.println("main......end....." + future.get());}
}

执行结果:

main......start.....
当前线程:20
运行结果:5
main......end.....10

2.4 线程串行化方法

在这里插入图片描述
假设B线程需要A线程执行完成之后才能继续执行,则需要线程串行化。

  • thenRun系列方法,不需要依赖A返回的结果,没有返回值。
  • thenAccept系列方法,需要依赖A返回的结果,没有返回值。
  • thenApply系列方法,依赖A返回的结果,有返回值。

具体看三类方法的参数也能看出来:
thenRun系列方法,只传入Runnable行为(不依赖结果),返回值类型是void(无返回值)。

public CompletableFuture<Void> thenRun(Runnable action)

thenAccept系列方法,传入Consumer<? super T> action,只做消费处理(依赖结果),返回值类型是void(无返回值)。

public CompletableFuture<Void> thenAccept(Consumer<? super T> action)

thenApply系列方法,传入Function<? super T,? extends U> fn,函数处理(依赖结果),返回值类型是U(有返回值)。

public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)

2.5 两任务组合 - 都要完成

在这里插入图片描述
在这里插入图片描述
两个任务必须都完成,触发该任务

  • runAfterBoth:组合两个 future,不需要获取 future 的结果,只需两个 future 处理完任务后,处理该任务。
  • thenAcceptBoth:组合两个 future,获取两个 future 任务的返回结果,然后处理任务,没有返回值。
  • thenCombine:组合两个 future,获取两个 future 的返回结果,并返回当前任务的返回值。

2.6 两任务组合 - 一个完成

在这里插入图片描述
在这里插入图片描述
当两个任务中,任意一个 future 任务完成的时候,执行任务

  • runAfterEither:两个任务有一个执行完成,不需要获取 future 的结果,处理任务,也没有返
    回值。
  • acceptEither:两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值。
  • applyToEither:两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值。

2.7 多任务组合

在这里插入图片描述
allOf:等待所有任务完成。
anyOf:只要有任何一个任务完成。

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

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

相关文章

机器学习周记(第三十三周:文献阅读[GWO-GART])2024.4.1~2024.4.7

目录 摘要 ABSTRACT 1 论文信息 1.1 论文标题 1.2 论文摘要 1.3 论文数据集 1.4 论文模型 2 相关知识 摘要 本周阅读了一篇使用GAT结合GRU预测PM2.5浓度的文章。论文模型为图注意力循环网络&#xff08;GART&#xff09;&#xff0c;首次提出了一种新型的多层GAT架构&…

AI预测福彩3D第27弹【2024年4月5日预测--第4套算法重新开始计算第12次测试】

今天继续按照合并后的算法进行测试&#xff0c;因为本套算法的命中率较高。以后有时间的话会在第二篇文章中发布排列3的预测结果。好了&#xff0c;废话不多说了&#xff0c;先上预测结果图&#xff0c;再上综合预测结果~ 2024年4月5日福彩3D的七码预测结果如下 第一套…

关于代码审查的一些思考

作为一名代码审查员&#xff0c;首先我们已经具备了丰富的代码开发经验&#xff0c;并且对提交的代码工程非常熟悉 代码审查可以发现并纠正代码中的错误、缺陷和不良实践。通过多人对代码进行仔细的检查和讨论&#xff0c;能够发现一些单独开发时难以察觉的问题&#xff0c;从…

5G智慧水利数字孪生可视化平台,推进水利行业数字化转型

5G智慧水利数字孪生可视化平台&#xff0c;推进水利行业数字化转型。随着5G技术的快速发展&#xff0c;越来越多的行业开始探索数字化转型的道路。水利行业作为国民经济的重要支柱&#xff0c;也面临着数字化转型的迫切需求。5G智慧水利数字孪生可视化平台作为水利行业数字化转…

Integer的缓存机制

LeetCode练习题--567.字符串的排列 今天刷题的时候,突然发现了一个问题: 为什么明明是相同的Integer值,有的时候使用""就可以,有的时候则必须使用equals方法来进行判断??? 于是我开始在网上查阅资料,几经无果,我开始阅读源码,一段时间后我才知道:原来Integer还有…

global关键字

global关键字 如果你想在局部作用域中修改全局变量&#xff0c;可以基于global关键字进行实现 默认情况下&#xff0c;在局部变量作用域只能对全局变量进行&#xff1a; 读取和修改内部元素&#xff08;可变类型&#xff09;&#xff0c;无法对全局变量进行重新赋值 读取 …

ZS卧式不锈钢离心泵

一、结构与设计特点ZS卧式不锈钢离心泵是一种高效能、耐腐蚀的泵类设备&#xff0c;其核心结构包括电机、泵体、叶轮、轴封和底座等部分。泵体采用优质不锈钢材料&#xff0c;确保了良好的耐蚀性和强度&#xff0c;同时&#xff0c;流道设计优化&#xff0c;减少了流动损失&…

【python基础教程】6 表达式

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;python基础教程 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、…

Canon CMOS图像传感器应用和选型

一、CCD和CMOS图像传感器 常见的数字感光元件有两种&#xff1a;CCD&#xff08;电荷耦合器件&#xff09;和CMOS&#xff08;互补金属氧化物半导体&#xff09;。1980年代&#xff0c;CCD进入消费级市场&#xff0c;并长期占据中高端市场。CMOS图像传感器最初作为廉价、低画质…

开启虚拟机时出现此主机支持 Intel VT-x,但 Intel VT-x 处于禁用状态怎么解决

问题描述 虚拟机安装完成后&#xff0c;点击开启此虚拟机弹出系统提示 原因分析&#xff1a; Intel VT-x 处于禁用状态&#xff0c;需要开启。 解决方案&#xff1a; 以联系小新笔记本电脑为例&#xff0c;进入BIOS界面&#xff0c;将Intel Virtual Technology设置成Enabl…

Vue+OpenLayers7入门到实战:OpenLayers如何销毁已经创建好的地图容器

返回《Vue+OpenLayers7》专栏目录:Vue+OpenLayers7入门到实战 前言 本章介绍如何使用OpenLayers7在地图上如何销毁已经创建好的地图容器。 在某些场景下,可能会需要销毁之前的地图,重新创建新的地图的需要,因此本章介绍一下在开始创建地图前如何先销毁之前的地图的功能。…

移动机器人运动规划 | 基于图搜索的Dijkstra 和 A*算法详解

Dijkstra 算法 Dijkstra 算法与BFS算法的区别就是 : 从容器中弹出接下来要访问的节点的规则不同 BFS 弹出: 层级最浅的原则&#xff0c;队列里最下方的元素 Dijkstra 弹出: 代价最小的节点g(n) g(n) :表示的是从开始节点到当前n节点的代价累加 Dijkstra在扩展的时候&#x…

利用native的方式实现跨线程调用

简介 在OpenHarmony应用开发实践中&#xff0c;经常会遇到一些耗时的任务&#xff0c;如I/O操作、域名解析以及复杂计算等。这些任务如果直接在主线程中执行&#xff0c;将会严重阻塞主线程&#xff0c;影响后续任务的正常流程&#xff0c;进而导致用户界面响应延迟甚至卡顿。…

【鸿蒙 HarmonyOS】获取设备的地理位置

一、背景 获取移动设备的地理位置&#xff0c;包含&#xff1a;经度、维度、具体地理位置等&#xff0c;地理位置信息能在许多业务场景中被应用&#xff0c;如导航、地图服务、位置服务、社交媒体等。 下面以一个Demo例子&#xff0c;来实现获取设备地理位置的功能 官方文档…

软件测试下的AI之路(4)

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;【Austin_zhai】 &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xff0c;分享行业相关最新信息。…

实时计算平台设计方案:913-基于100G光口的DSP+FPGA实时计算平台

基于100G光口的DSPFPGA实时计算平台 一、产品概述 基于以太网接口的实时数据智能计算一直应用于互联网、网络安全、大数据交换的场景。以DSPFPGA的方案&#xff0c;体现了基于硬件计算的独特性能&#xff0c;区别于X86GPU的计算方案&#xff0c;保留了高带宽特性&…

Ceph学习 - 1.存储知识

文章目录 1.存储基础1.1 基础知识1.1.1 存储基础1.1.2 存储使用 1.2 文件系统1.2.1 简介1.2.2 数据存储1.2.3 存储应用的基本方式1.2.4 文件存储 1.3 小结 1.存储基础 学习目标&#xff1a;这一节&#xff0c;我们从基础知识、文件系统、小节三个方面来学习。 1.1 基础知识 1.…

UART设计

一、UART通信简介 通用异步收发器&#xff0c; 特点&#xff1a;串行、异步、全双工通信 优点&#xff1a;通信线路简单&#xff0c;传输距离远 缺点&#xff1a;传输速度慢 数据传输速率&#xff1a;波特率&#xff08;单位&#xff1a;baud&#xff0c;波特&#xff09; …

如何高效学习Python编程语言

理解Python的应用场景 不同的编程语言有不同的发展历史和应用场景,了解Python主要应用在哪些领域对于学习它会有很大帮助。Python最初是一种通用脚本语言,主要用于系统级任务自动化。随着时间的推移,它逐步成为数据处理、科学计算、Web开发、自动化运维等众多领域的主要编程语…

Navicat设置mysql权限

新建用户&#xff1a; 注意&#xff1a;如果不生效执行刷新命令:FLUSH PRIVILEGES; 执行后再重新打开查看&#xff1b; 查询权限命令&#xff1a;1234为新建的用户名&#xff0c;localhost为访问的地址 SHOW GRANTS FOR 1234localhost;如果服务器设置服务器权限后可能会出现权…