商城-学习整理-高级-商城业务-异步线程池(十三)

news2024/11/24 7:48:21

目录

  • 一、线程
    • 1、初始化线程的 4 种方式
    • 2、线程池的七大参数
    • 3、线程池的运行流程:
    • 4、例子
    • 5、常见的 4 种线程池
    • 6、开发中为什么使用线程池
  • 二、CompletableFuture 异步编排
    • 0、业务场景:
    • 1、创建异步对象
    • 2、计算完成时回调方法
    • 3、handle 方法
    • 4、线程串行化方法
    • 5、两任务组合 - 都要完成
    • 6、两任务组合 - 一个完成
    • 7、多任务组合
  • 三、测试代码

一、线程

1、初始化线程的 4 种方式

1)、继承 Thread
2)、实现 Runnable 接口
3)、实现 Callable 接口 + FutureTask (可以拿到返回结果,可以处理异常)
4)、线程池

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

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

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

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
拒绝策略,如果线程满了,线程池就会使用拒绝策略。

在这里插入图片描述

3、线程池的运行流程:

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

4、例子

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

5、常见的 4 种线程池

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

6、开发中为什么使用线程池

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

二、CompletableFuture 异步编排

0、业务场景:

查询商品详情页的逻辑比较复杂,有些数据还需要远程调用,必然需要花费更多的时间。
在这里插入图片描述
假如商品详情页的每个查询,需要如下标注的时间才能完成

那么,用户需要 5.5s 后才能看到商品详情页的内容。很显然是不能接受的。

如果有多个线程同时完成这 6 步操作,也许只需要 1.5s 即可完成响应。

Future是java5添加的类,用来描述一个异步计算的结果。你可以使用isDone方法检查计算是否完成,或者使用get阻塞住调用线程,知道计算完成返回结果,你也可以使用canel方法停止任务的执行。

虽然Future以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和我们的异步编程的初衷相违背,轮询的方式又会耗费无谓的 CPU 资源,而且也不能及时地得到计算结果,为什么不能用观察者设计模式当计算结果完成及时通知监听者呢?

很多语言,比如 Node.js,采用回调的方式实现异步编程。Java 的一些框架,比如 Netty,自己扩展了 Java 的 Future接口,提供了addListener等多个扩展方法;Google guava 也提供了通用的扩展 Future;Scala 也提供了简单易用且功能强大的 Future/Promise 异步编程模式。

作为正统的 Java 类库,是不是应该做点什么,加强一下自身库的功能呢?

在 Java 8 中, 新增加了一个包含 50 个方法左右的类: CompletableFuture,提供了非常强大的Future 的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果,并且提供了转换和组合 CompletableFuture 的方法。

CompletableFuture 类实现了 Future 接口,所以你还是可以像以前一样通过get方法阻塞或者轮询的方式获得结果,但是这种方式不推荐使用。

CompletableFuture 和 FutureTask 同属于 Future 接口的实现类,都可以获取线程的执行结果。
在这里插入图片描述

1、创建异步对象

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

2、计算完成时回调方法

在这里插入图片描述
whenComplete 可以处理正常和异常的计算结果,exceptionally 处理异常情况。

whenComplete 和 whenCompleteAsync 的区别:

whenComplete:是执行当前任务的线程,继续执行 whenComplete 的任务。

whenCompleteAsync:是执行把 whenCompleteAsync 这个任务继续提交给线程池来进行执行。

方法不以 Async 结尾,意味着 Action 使用相同的线程执行,而 Async 可能会使用其他线程执行(如果是使用相同的线程池,也可能会被同一个线程选中执行)

public class CompletableFutureDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture future = CompletableFuture.supplyAsync(new Supplier<Object>() {
@Override
public Object get() {
System.out.println(Thread.currentThread().getName() + "\t
completableFuture");
int i = 10 / 0;
return 1024;
}
}).whenComplete(new BiConsumer<Object, Throwable>() {
@Override
public void accept(Object o, Throwable throwable) {
System.out.println("-------o=" + o.toString());
System.out.println("-------throwable=" + throwable);
}
}).exceptionally(new Function<Throwable, Object>() {
@Override
public Object apply(Throwable throwable) {
System.out.println("throwable=" + throwable);
return 6666;
}
});
System.out.println(future.get());
}
}

3、handle 方法

在这里插入图片描述
和 complete 一样,可对结果做最后的处理(可处理异常),可改变返回值。

4、线程串行化方法

在这里插入图片描述thenApply 方法:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前任务的返回值。
thenAccept 方法:消费处理结果。接收任务的处理结果,并消费处理,无返回结果。
thenRun 方法:只要上面的任务执行完成,就开始执行 thenRun,只是处理完任务后,执行thenRun 的后续操作

带有 Async 默认是异步执行的。同之前。

以上都要前置任务成功完成。

Function<? super T,? extends U>
T:上一个任务返回结果的类型
U:当前任务的返回值类型

5、两任务组合 - 都要完成

在这里插入图片描述
在这里插入图片描述
两个任务必须都完成,触发该任务。
thenCombine:组合两个 future,获取两个 future 的返回结果,并返回当前任务的返回值
thenAcceptBoth:组合两个 future,获取两个 future 任务的返回结果,然后处理任务,没有返回值。
runAfterBoth:组合两个 future,不需要获取 future 的结果,只需两个 future 处理完任务后,处理该任务。

6、两任务组合 - 一个完成

在这里插入图片描述
在这里插入图片描述
当两个任务中,任意一个 future 任务完成的时候,执行任务。
applyToEither:两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值。
acceptEither:两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值。
runAfterEither:两个任务有一个执行完成,不需要获取 future 的结果,处理任务,也没有返回值。

7、多任务组合

在这里插入图片描述
在这里插入图片描述

三、测试代码

import java.util.concurrent.*;

public class ThreadTest {

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // System.out.println("main......start.....");
        // Thread thread = new Thread01();
        // thread.start();
        // System.out.println("main......end.....");

        // Runable01 runable01 = new Runable01();
        // new Thread(runable01).start();

        // FutureTask<Integer> futureTask = new FutureTask<>(new Callable01());
        // new Thread(futureTask).start();
        // System.out.println(futureTask.get());

        // service.execute(new Runable01());
        // Future<Integer> submit = service.submit(new Callable01());
        // submit.get();

        System.out.println("main......start.....");
        // CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
        //     System.out.println("当前线程:" + Thread.currentThread().getId());
        //     int i = 10 / 2;
        //     System.out.println("运行结果:" + i);
        // }, executor);

        /**
         * 方法完成后的处理
         */
        // 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;
        // });

        /**
         * 方法执行完后端处理
         */
        // 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;
        // });


        /**
         * 线程串行化
         * 1、thenRunL:不能获取上一步的执行结果
         * 2、thenAcceptAsync:能接受上一步结果,但是无返回值
         * 3、thenApplyAsync:能接受上一步结果,有返回值
         *
         */
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果:" + i);
            return i;
        }, executor).thenApplyAsync(res -> {
            System.out.println("任务2启动了..." + res);
            return "Hello" + res;
        }, executor);
        System.out.println("main......end....." + future.get());

    }

    private static void threadPool() {

        ExecutorService threadPool = new ThreadPoolExecutor(
                200,
                10,
                10L,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<Runnable>(10000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

        //定时任务的线程池
        ExecutorService service = Executors.newScheduledThreadPool(2);
    }


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


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


    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;
        }
    }

}

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

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

相关文章

springboot 项目日志配置文件详解

spring boot 项目指定 日志配置文件 在Spring Boot项目中&#xff0c;可以通过在application.properties或application.yml文件中指定日志配置文件来配置日志。 1. 使用application.properties文件&#xff1a; 在application.properties中&#xff0c;您可以使用以下属性来…

系统卡死问题分析

CPU模式 CPU Frequency Scaling (CPUFREQ) Introduction CPU频率调节设备驱动程序的功能。该驱动程序允许在运行过程中更改CPU的时钟频率。一旦CPU频率被更改,必要的电源供应电压也会根据设备树脚本(DTS)中定义的电压值进行变化。通过降低时钟速度,这种方法可以减少功耗…

C语言刷题训练DAY.9

1.线段图案 解题思路&#xff1a; 这里非常简单&#xff0c;我们只需要用一个循环控制打印即可。 解题代码&#xff1a; #include<stdio.h> int main() {int n 0;while ((scanf("%d", &n)) ! EOF){int i 0;for (i 0; i < n; i){printf("*&…

js闭包用法以及和bind的结合使用

bind用法 let info { name: "xuhaitao", age: 36 }function haitao() {console.log(this);}let fun haitao.bind(info)fun();haitao(); 控制台打印: 闭包用法: function xiaoMing() {let v 1;function jia() {v;console.log(v);}function getV() {console.log(…

【C语言】每日一题(单词倒排)

单词倒排&#xff0c;链接奉上。 方法 做题前的预备知识双指针逆序整个逆序单词 做题前的预备知识 在做题时遇到有关判断字母与数字时&#xff0c;因为总会写成str>0&&str<9之类的形式&#xff0c;比较繁琐&#xff0c;而C语言为了解决这个问题&#xff0c;有了…

线段树详解——影子宽度

OK&#xff0c;今天来讲一讲线段树~~ 线段树是什么线段树的实现线段树的时间复杂度线段树的应用线段树的节点结构其他操作和优化例题——影子宽度输入输出格式输入格式输出格式 输入输出样例输入样例输出样例 例题讲解 线段树是什么 线段树&#xff08; S e g m e n t Segmen…

【设计原则】图解何为依赖倒置

依赖倒置原则&#xff08;Dependence Inversion Principle&#xff0c;DIP&#xff09;是指设计代码结构时&#xff0c;高层模块不应该依赖低层模块&#xff0c;二者都应该依赖其抽象。 要理解何为倒置&#xff0c;那就先得明确什么是“正向”&#xff0c;可以看到下图代码是自…

Element Plus <el-table> 组件之展开行Table在项目中使用

目录 官方样式&#xff1a; 展开前&#xff1a; 展开&#xff1a; 原始代码&#xff1a; 代码详解&#xff1a; 项目使用场景&#xff1a; 完成效果&#xff1a; 具体实现范本&#xff1a; 1.调整数据结构 2. 修改标签和数据绑定 3. JavaScript 部分导入和创建对象 …

浅谈日常使用的 Docker 底层原理-三大底座

适合的读者&#xff0c;对Docker有过简单了解的朋友&#xff0c;想要进一步了解Docker容器的朋友。 前言 回想我这两年&#xff0c;一直都是在使用 Docker&#xff0c;看过的视频、拜读过的博客&#xff0c;大都是在介绍 Docker 的由来、使用、优点和发展趋势&#xff0c;但对…

路由跳转--编程式导航

简介 除了使用 创建 a 标签来定义导航链接&#xff0c;我们还可以通过编程式导航实现导航。所谓编程式导航指的是不通过router-link跳转&#xff0c;而是借助 router 的实例&#xff0c;通过代码的方式跳转。 示例&#xff1a; App.vue <template><div id"ap…

正演的数值模拟(零基础,学习中)

摘要: 本贴从零开始学习正演的数值模拟方法. 1. 偏微分基础 引例: 物体从一维坐标的原点开始移动, 在 t t t 时刻, 它在坐标轴的位置由函数 s ( t ) s(t) s(t) 确定, 则速度为位置变化量与时间的比值: v ( t ) d s ( t ) d t lim ⁡ Δ t → 0 s ( t Δ t ) − s ( t )…

HDFS存储魔法解析:在二次元世界中跃动的数据冒险

文章目录 版权声明零 引缘起一 存储原理二 fsck命令2.1 副本块数量的配置2.1.1 全局设置方式2.1.2 临时设置方式 2.2 检查文件的副本数2.3 block大小和复制策略配置 三 NameNode元数据3.1 edits文件3.2 fsimage文件3.3 NameNode元数据管理维护3.4 元数据合并控制参数3.5 Checkp…

热门歌曲的伴奏--源代码

目录 使用方法 《起风了》歌词伴奏 《起风了》歌词 《生日歌》歌词伴奏 《生日歌》歌词 《童话》歌词伴奏 《童话》歌词 《光阴的故事》歌词伴奏 《光阴的故事》歌词 ​《千本樱》歌词伴奏 《千本樱》中文歌词 《平凡之路》歌词伴奏 《平凡之路》歌词 《孤勇者》…

Linux-在Ubuntu搭建ftp服务器

By: Ailson Jack Date: 2023.08.20 个人博客&#xff1a;http://www.only2fire.com/ 本文在我博客的地址是&#xff1a;http://www.only2fire.com/archives/151.html&#xff0c;排版更好&#xff0c;便于学习&#xff0c;也可以去我博客逛逛&#xff0c;兴许有你想要的内容呢。…

EndNote(一)【界面+功能介绍】

EndNote界面&#xff1a; 顶上小图标的介绍&#xff1a; ①&#xff1a;同步 ②&#xff1a;分享 ③&#xff1a;检索全文 对于第三个&#xff08;检索全文的功能&#xff09;&#xff1a; &#xff08;不做任何操作的情况下的界面&#xff0c;检索全文的按钮是灰的&…

详解:Mybatis参数获取和动态SQL以及分页功能

详解&#xff1a;Mybatis参数获取和动态SQL以及分页功能 前置准备项目结构在pom文件导入依赖创建properties配置文件创建Mapper接口创建Mapper映射文件SqlSession对象创建对应的表在数据库中实体类 SQL语句中的参数获取单个参数两个参数比较参数找不到的情况单个参数&#xff0…

C语言小练习(一)

&#x1f31e; “人生是用来体验的&#xff0c;不是用来绎示完美的&#xff0c;接受迟钝和平庸&#xff0c;允许出错&#xff0c;允许自己偶尔断电&#xff0c;带着遗憾&#xff0c;拼命绽放&#xff0c;这是与自己达成和解的唯一办法。放下焦虑&#xff0c;和不完美的自己和解…

自动方向识别式 TXB型电平转换芯片

大家好,这里是大话硬件。 在上一篇文章分析了LSF型的电平转换芯片,LSF型电平转换芯片最常见是应用在I2C总线上。I2C为OD型总线,LSF使用时增加电阻。 对于不是OD型总线的电平转换,比如UART,SPI,普通GPIO口信号,这些信号在进行双向电平转换使用什么样的芯片呢? 从上面…

Python写一个创意五子棋游戏

前言 在本教程中&#xff0c;我们将使用Python的Tkinter库和OpenAI的GPT-3模型构建一个简单的对话弹窗软件&#xff0c;用于与老板进行对话。我们将介绍如何创建图形用户界面、集成OpenAI API以生成回复&#xff0c;并提供一些进一步的扩展和优化建议。 &#x1f4dd;个人主页→…

岩土工程安全监测隧道中使用振弦采集仪注意要点?

岩土工程安全监测隧道中使用振弦采集仪注意要点&#xff1f; 岩土工程的安全监测是非常重要的&#xff0c;它可以帮助工程师及时发现可能存在的问题&#xff0c;并及时解决&#xff0c;保障施工进度以及施工质量&#xff0c;保障工程的安全运行。其中&#xff0c;振弦采集仪是…