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

news2024/12/26 0:42:39

一、线程回顾

1.1 初始化线程的 4 种方式

1)、继承 Thread

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

2)、实现 Runnable 接口

public static class Runable01 implements Runnable {
        @Override
        public 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> {
        @Override
        public 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.coloradmin.cn/o/1574268.html

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

相关文章

十分钟搞定TCP三次握手面试

三次握手过程 1.客户端与客户端都处于close状态&#xff0c;服务器主动对某端口进行监听后处于LISTEN状态 2.客户端将SYN标志位置为1&#xff0c;向服务器发,SYN和初始序列号seq后处于SYN_SENT状态 2.服务器处于LISTEN状态&#xff0c;收到客户端发来的请求后将SYN和ACK的标志…

基于DCT和扩频的音频水印嵌入提取算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ......................................................................... N 10; %嵌入一…

ubuntu安装编程字体DejaVu Sans Mono

DejaVu Sans Mono 安装命令&#xff1a; sudo apt-get install ttf-dejavu

唐刘:关于产品质量的思考 - 如何评估质量

在上一篇文章《 关于产品质量的思考 - 我的基本认知 》中&#xff0c;作者通过亲身经历分享了对产品质量的思考和认知&#xff1a;高质量的产品不仅仅是通过测试来保证的&#xff0c;更是通过在真实场景中不断打磨和改进得来的。本文为“关于产品质量的思考”系列的第二篇&…

2024.4.2-day07-CSS 盒子模型(显示模式、盒子模型)

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 作业 2024.4.2 学习笔记CSS标签元素显示模式1 块元素2 行内元素3 行内块元素4…

每日OJ题_优先级队列_堆③_力扣692. 前K个高频单词

目录 力扣692. 前K个高频单词 解析代码 力扣692. 前K个高频单词 692. 前K个高频单词 难度 中等 给定一个单词列表 words 和一个整数 k &#xff0c;返回前 k 个出现次数最多的单词。 返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率&#xff0c…

技术再度取得优势,人工智能兴起推动需求,美芯涨价收割市场,收割中国制造?...

独家首发 ------------- 分析机构指出一季度全球存储芯片涨价了15%左右&#xff0c;而近期三星半导体预测全球存储芯片的价格还将继续上涨&#xff0c;预计二季度至少上涨两成&#xff0c;显示出美系芯片在忍受了一年多的亏损之后再度联手涨价。 2022年中国存储芯片取得了重大进…

数据流图

数据字典 数据流图平衡原则 父图与子图之间的平衡子图内平衡

IP地址到底有什么用

IP地址在计算机网络中的作用至关重要&#xff0c;它不仅是设备在网络中的唯一标识&#xff0c;更是实现网络通信、网络管理和安全的关键要素。下面&#xff0c;我们将从多个方面详细阐述IP地址的作用。 首先&#xff0c;IP地址作为设备的唯一标识&#xff0c;为网络通信提供了…

Leetcode 17.电话号码的字母组合

题目 思路 输入的digits有几个数就有几层。 一层中有几个数则取决于输入的数字对应的字母有几个。 1.确定递归函数的返回值及参数&#xff1a; 其实参数不是一开始就确定好的&#xff0c;而是你在写递归函数的时候缺啥&#xff0c;就往进去传啥。 这里我就直接全部写出来。…

什么是物联网云平台

在信息化和智能化浪潮的推动下&#xff0c;物联网云平台作为连接物理世界与数字世界的桥梁&#xff0c;正日益成为企业数字化转型的关键支撑。物联网云平台通过集成先进的云计算、大数据分析和人工智能等技术&#xff0c;为企业提供了高效、安全、智能的数据处理和应用服务&…

爬虫实战一、Scrapy开发环境(Win10+Anaconda3)搭建

#前言 在这儿推荐使用Anaconda进行安装&#xff0c;并不推荐大家用pythonpip安装&#xff0c;因为pythonpip的坑实在是太多了。 #一、环境中准备&#xff1a; Win10&#xff08;企业版&#xff09;Anaconda3-5.0.1-Windows-x86_64&#xff0c;下载地址&#xff0c;如果打不开…

Linux 著名的sudo、su是什么?怎么用?

一、su 什么是su&#xff1f; su命令&#xff08;简称是&#xff1a;substitute 或者 switch user &#xff09;用于切换到另一个用户&#xff0c;没有指定用户名&#xff0c;则默认情况下将以root用户登录。 为了向后兼容&#xff0c;su默认不改变当前目录&#xff0c;只设…

记录Ubuntu安装yum报错解决方法

安装问题分析 首先&#xff1a;分析一下&#xff0c;你按照别的博客安装yum出现的的大部分问题&#xff0c;都是说是在软件包里面无法定位yum&#xff0c;如下图一样&#xff0c;想必应该是这样的&#xff01;&#xff01;如果不是这个问题&#xff0c;放心这篇博客也可以帮你解…

可视化场景(9):智慧看板,可能是最直观的数据展示

10年经验的大数据可视化和数字孪生老司机&#xff0c;该领域的专家&#xff0c;是您可信赖的技术合伙人&#xff0c;分享该领域的项目和作品&#xff0c;欢迎互动交流。 hello&#xff0c;我是贝格前端工场&#xff0c;本期分享可视化大屏在安全生产与设备运维场景的应用&#…

【Web】纯萌新的BUUCTF刷题日记Day1

目录 [RoarCTF 2019]Easy Java [网鼎杯 2018]Fakebook [CISCN2019 华北赛区 Day2 Web1]Hack World [BJDCTF2020]The mystery of ip [网鼎杯 2020 朱雀组]phpweb [BSidesCF 2020]Had a bad day [BJDCTF2020]ZJCTF&#xff0c;不过如此 [BUUCTF 2018]Online Tool [GXYCTF…

pringboot2集成swagger2出现guava的FluentIterable方法不存在

错误信息 Description: An attempt was made to call a method that does not exist. The attempt was made from the following location: springfox.documentation.spring.web.scanners.ApiListingScanner.scan(ApiListingScanner.java:117) The following method did not ex…

解决Idea导入项目:Unable to import maven project: See logs for details

2019.1版本idea&#xff0c;使用3.9.4版本maven&#xff0c;导入项目出现如下问题&#xff1a; 1.尝试修改配置参数 2.参数修改没有效果&#xff0c;查看具体日志 可以看到如下报错信息 3.解决办法 猜想Maven与IDEA版本不一致导致 由高版本&#xff1a;apache-maven-3.9.4 降…

C++11: 右值引用,移动语义,万能引用,完美转发,新的默认成员函数

C11: 右值引用,移动语义,万能引用,完美转发,新的默认成员函数 一.左值和右值1.左值2.右值3.左值,右值和能否被修改的关系 二.左值引用的好处和局限1.完全解决了传值传参时的深拷贝问题2.传引用返回时需要注意的点1.坑点:传引用返回用值接收2.传引用返回用引用接收3.应该怎么办?…

智慧工厂EMS能效管理解决方案

安科瑞电气股份有限公司 祁洁 15000363176 一、传统工厂现状 1、缺乏顶层设计&#xff0c;智慧化建设碎片化&#xff0c;不成体系&#xff0c;建成即落后。 2、弱电系统、网络、数据中心等基础设施老化&#xff0c;服务感知差。 3、缺乏设备在线监视&#xff0c;无法及时…