线程池怎么用?---实例讲解

news2025/2/24 11:05:44

线程池使用实例

先写一个配置类

/**
 * 线程池配置
 */
@Configuration
public class ThreadPoolConfig {

    //定义线程前缀
     public static final String NAME_PRE="test";

    /**
     * ExecutorService 这个对象就是线程池,可以点进去他的源码看看
     * @Bean,将getExecutor()方法返回的对象交给Spring管理
     */
    @Bean
    public static ExecutorService getExecutor(){
        
        /**
         *方法一
         *ExecutorBuilder e=new ExecutorBuilder();
         *e.setAllowCoreThreadTimeOut(false);
        **/
        
        /**方法二直接Builder赋值**/
        return ExecutorBuilder.create()
                .setCorePoolSize(8)
                .setMaxPoolSize(16)
                .setKeepAliveTime(60, TimeUnit.SECONDS)
                .setHandler(new ThreadPoolExecutor.CallerRunsPolicy())
                .setWorkQueue(new LinkedBlockingDeque<>(2000))
                .setThreadFactory(ThreadFactoryBuilder.create().setNamePrefix(NAME_PRE).build())
                .setAllowCoreThreadTimeOut(false)
                .build();
    }
}
  • ExecutorService 这个对象是线程池
  • @Bean,在项目启动时执行ExecutorService类的方法,返回一个对象交给spring管理。
  • @Configuration,配置注解,在项目启动时,首先执行这个类的代码。
  • 返回的对象第一种方法用set方法去赋值;第二种用Builder赋值。

参数内容为数字也通常为魔法值

方法1、可以放到配置文件中(yml配置)
threadpool:
  corepoolsize: 8
  maxPoolSize: 16
  # 队列大小
  dequesize: 2000
  # 线程前缀
  namepre: test-
方法2、设置静态常量
//定义线程前缀
public static final String NAME_PRE="test";

.setThreadFactory(ThreadFactoryBuilder.create().setNamePrefix(NAME_PRE).build())

这样一个线程池就建好了

写好了怎么用呢?

由于ExecutorService是一个interface,且交给了spring管理,所以直接注入使用。 这里在Impl注入。

@Resource
private ExecutorService executorService;

写一个测试尝试一下:

Service中写接口
/**Service中写接口**/

void test() throws InterruptedException;

Impl中重写方法(先模拟不用线程池)

使用@SentinelResource 注解 value指定资源的名称,blockHandler用于指定服务限流后的后续处理逻辑。

依赖:

<!--    如果要使用@SentinelResource必须添加此依赖    -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-annotation-aspectj</artifactId>
    <version>1.8.1</version>
</dependency>
/**Impl中重写方法**/

@SentinelResource(value = "test",blockHandler="exceptionHandler")
@Override
public Boolean test() throws InterruptedException {
    /**模拟业务场景耗时**/
    Thread.sleep(100000);
    return true;
}
controller中调用接口
/**controller中调用接口**/

@GetMapping("/open/test")
public Boolean test() throws InterruptedException {
    userService.test();
    return Boolean.TRUE;
}

我们可以看到,由于睡眠操作,访问接口一直显示加载状态

0

怎么用线程池?两个方法

方法一 注解方式(用的不多)

方法二 使用CompletableFuture

@Override
public void test() throws InterruptedException {
    log.info("进来了");
    CompletableFuture.runAsync(()->{
        try {
            log.info("进来了1111");
            /**模拟业务场景耗时**/
            Thread.sleep(100000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    },executorService);

}

浏览器访问会立即打开

0

后台日志也表明已将任务交给线程池

0

多执行几次看看

0

我们发现,线程池到 test7 后就没有了,因为我们设置的核心线程数为8,这8个线程都在帮我们干活,再多的线程任务将放置到队列中。

等一小会....

发现刚刚被放入队列未执行的任务又被执行了,因为有线程执行完任务,就去队列中拿新任务去执行了。

0

使用多线程的三种形式

1、将任务交给线程池去做,至于成不成功、需不需要返回值,不关注。

例如新增用户的三个任务交给线程池,不关注有没有成功。当主线程将所有任务交给线程池后,主线程就认为新增用户这个任务完成了,去进行下一项任务。

===> 适用于更新操作,不关注返回值

2、将任务交给线程池去做,需要等待任务返回的结果。当主线程将任务交给线程池后,进入阻塞状态,直到获得所有的返回值。

例如将新增用户改为查询用户信息,我们知道查询一定是要返回结果的,所以主线程需要等待线程池内的任务执行完毕,他才能继续下一项任务,在这期间主线程一直处于阻塞状态。

===> 适用于多IO操作,如:for循环查数据库、循环调用http接口查询、多次查询数据库操作

模拟一下多线程查询

@Override
public void test() throws InterruptedException {
    log.info("进来了");
    
    //QueryBo为构建的返回值
    QueryBO queryBO=new QueryBO();
    
    //获取开始时间
    long start = System.currentTimeMillis();
    
    //创建线程,将任务放到线程池
    CompletableFuture<Void> query1 = CompletableFuture.runAsync(() -> {
        Boolean aBoolean = null;
        try {
            aBoolean = query1();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        queryBO.setQuery1(aBoolean);
    }, executorService);

    //创建线程,将任务放到线程池
    CompletableFuture<Void> query2 =CompletableFuture.runAsync(()-> {
        Boolean aBoolean = null;
        try {
            aBoolean = query2();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        queryBO.setQuery2(aBoolean);
    },executorService);

    //创建线程,将任务放到线程池
    CompletableFuture<Void> query3=CompletableFuture.runAsync(()-> {
        Boolean aBoolean = null;
        try {
            aBoolean = query3();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        queryBO.setQuery3(aBoolean);
    },executorService);
    
    //将这3个任务放到数组中
    CompletableFuture[] completableFutures=Stream.of(query1,query2,query3).collect(Collectors.toList()).toArray(new CompletableFuture[3]);
    
    //当数组里的任务都执行完,聚合一下,这行代码相当于将主线程阻塞住
    CompletableFuture.allOf(completableFutures).join();
    
    //打印时间戳
    long time = System.currentTimeMillis()-start;
    
    //allOf之后才会执行这句log,验证一下queryBO
    log.info("查询结果{},时间差{}",queryBO,time);
}

/**
 * 第一个查询,耗时3秒
 * @return
 * @throws InterruptedException
 */
public Boolean query1()throws InterruptedException{
    log.info("进来了1");
    Thread.sleep(3000);
    return true;
}

/**
 * 第二个查询,耗时1秒
 * @return
 * @throws InterruptedException
 */
public Boolean query2()throws InterruptedException{
    log.info("进来了2");
    Thread.sleep(1000);
    return true;
}

/**
 * 第三个查询,耗时5s
 * @return
 * @throws InterruptedException
 */
public Boolean query3()throws InterruptedException{
    log.info("进来了3");
    Thread.sleep(5000);
    return false;
}

给一个QueryBO,Controller、Service代码都是不变的,上边有

@Data
public class QueryBO {
    private Boolean query1;
    private Boolean query2;
    private Boolean query3;
}

运行一下子,看看结果:

===> 不出意料的时间差是5秒左右,QueryBO中也有三个任务的返回值,说明三个查询是一起执行的。

===>重点代码是,以下两行,仔细研究

//将这3个任务放到数组中
CompletableFuture[] completableFutures=Stream.of(query1,query2,query3).collect(Collectors.toList()).toArray(new CompletableFuture[3]);

//当数组里的任务都执行完,聚合一下,这行代码相当于将主线程阻塞住
CompletableFuture.allOf(completableFutures).join();

线程不安全问题

定义一个全局变量count=1;每个线程内都去操作这个count,让它+1操作,最后count的结果是什么?

多执行几次结果:

===>发现这个count并不是连续递增,说明多线程操作一个变量是不安全的,可能会被互相覆盖。

===>解决方法:加锁

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

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

相关文章

【零基础入门Python】Python If Else流程控制

✍面向读者&#xff1a;所有人 ✍所属专栏&#xff1a;零基础入门Pythonhttps://blog.csdn.net/arthas777/category_12455877.html Python if语句 Python if语句的流程图 Python if语句示例 Python If-Else Statement Python if else语句的流程图 使用Python if-else语句 …

【EI稳定检索】第三届能源利用与自动化国际学术会议(ICEUA 2024)

第三届能源利用与自动化国际学术会议&#xff08;ICEUA 2024&#xff09; 2024 3rd International Conference on Energy Utilization and Automation (ICEUA 2024) ICEUA 2024已成功申请JPCS - Journal of Physics: Conference Series (ISSN:1742-6596)---独立出版 2024年…

【Linux】 OpenSSH_9.3p1 升级到 OpenSSH_9.5p1(亲测无问题,建议收藏)

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; &#x1f40b; 希望大家多多支…

模板、STL标准模板库

模板 通常 对 具有相同要求的结果或者类 提供一个模板&#xff0c;根据实际使用时传过来的数据类型&#xff0c;决定函数和类的具体实现。 模板可以让类或者函数支持一种类型&#xff0c;这种通用类型在实际运行的过程中可以使用任何数据类型。 这种编程方式也成为"泛型编…

uniapp自定义进度条组件

目标效果 原型设计为这样的样式&#xff0c;但是现有的进度条都无法满足需求&#xff0c;于是编写组件实现。 设计引用格式为 <zLineProgress :total"15" :val"7" title"你好吗" />定义组件 <template><view style"hei…

TikTok动态展示广告是什么?

TikTok 的动态展示广告 (DSA) 是一种定制视频广告&#xff0c;它们是根据广告模板实时创建的&#xff0c;并填充定期更新的产品目录中的产品信息。DSA 是 TikTok 版本的动态产品广告&#xff0c;是社交广告中受到卖家欢迎的一种形式&#xff0c;主要是在应用程序和社交广告平台…

【java+vue+微信小程序项目】从零开始搭建——健身房管理平台(1)spring boot项目搭建、vue项目搭建、微信小程序项目搭建

项目笔记为项目总结笔记,若有错误欢迎指出哟~ 【项目专栏】 【java+vue+微信小程序项目】从零开始搭建——健身房管理平台(1)项目搭建 持续更新中… java+vue+微信小程序项目】从零开始搭建——健身房管理平台 项目简介Java项目搭建(IDEA)1.新建项目2.项目类型3.项目设置4…

❀My学习Linux命令小记录(10)❀

目录 ❀My学习Linux命令小记录&#xff08;10&#xff09;❀ 36.fold指令 37.expr指令 38.iperf指令 39.telnet指令 40.ssh指令 ❀My学习Linux命令小记录&#xff08;10&#xff09;❀ 36.fold指令 功能说明&#xff1a;控制文件内容输出时所占用的屏幕宽度&#xff0c…

Android12之MediaCodec硬编解码调试手段(四十九)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只…

YOLOv8创新魔改教程(二)如何添加注意力机制

YOLOv8创新魔改教程&#xff08;二&#xff09;如何添加注意力机制 &#xff08;一&#xff09;找代码 github找各种注意力机制的代码 &#xff08;二&#xff09;融合 1.创建文件 在ultralytics/nn/attention.py创建attention.py 文件 将找到的代码粘贴进来 2.修改task…

MySQL find_in_set函数的深入解析与应用

theme: smartblue 在数据库操作中&#xff0c;我们经常会遇到需要处理以逗号分隔的字符串&#xff0c;并且需要根据这些字符串进行查询的情况。MySQL提供了一个非常实用的函数FIND_IN_SET()来处理这种特定的查询需求。本文将深入解析FIND_IN_SET()函数的使用方法&#xff0c;并…

Disucz论坛必备哪些插件,最全Disucz插件【2023最新】

社区论坛的重要性愈发凸显。Disucz作为一款开源的社区论坛软件&#xff0c;其灵活性和可扩展性使其成为许多网站社区的首选。要充分发挥Disucz的潜力&#xff0c;选择并安装适当的插件是至关重要的。 Disucz插件大全 Disucz插件的多样性和功能丰富性为用户提供了个性化和高度…

Centos系列:Centos7下部署nginx(三种方式安装部署,图文结合超详细,适合初学者)

Centos7下部署nginx&#xff08;三种方式安装部署&#xff0c;图文结合超详细&#xff0c;适合初学者&#xff09; Centos7下部署nginx一. ngxin是什么二. nginx的作用正向代理和反向代理的区别 三. 安装部署安装环境1. yum安装配置nginx源启动nginx浏览器访问&#xff0c; IP:…

详细了解 MOSFET 晶体管

MOSFET 开关晶体管 MOS 管是 “金属&#xff08;Metal&#xff09;氧化物&#xff08;Oxide&#xff09;半导体&#xff08;Semi&#xff09;” 场效应晶体管&#xff0c;或者称是 “金属&#xff08;Metal&#xff09;绝缘体&#xff08;Insulator&#xff09;半导体&#xf…

微信公众号端在线客服系统源码 聊天记录云端实时保存 附带完整的搭建教程

随着社交媒体的普及&#xff0c;越来越多的用户通过微信公众号与企业进行沟通。因此&#xff0c;开发一款基于微信公众号的在线客服系统&#xff0c;可以帮助企业更好地服务用户&#xff0c;提高客户满意度。同时&#xff0c;为了解决聊天记录的存储和管理问题&#xff0c;我们…

iOS17苹果备忘录怎么设置提醒?

在我们快节奏的生活中&#xff0c;苹果备忘录成了记录灵感、任务和重要事项的得力助手&#xff0c;面对着一个让人头疼的问题——备忘录竟然不能设置提醒&#xff01;突然感觉我的备忘录只是个寂寞的清单&#xff0c;没有提醒的陪伴。 于是&#xff0c;我着手寻找解决之道&…

02_W5500网络初始化

如何与W5500通信&#xff1f; 我们在W5500介绍中可以看到W5500支持SPI通信协议&#xff0c;如果对SPI通信协议还不太了解&#xff0c;请转 SPI数据帧&#xff1a; W5500 的 SPI 数据帧包括了 16 位地址段的偏移地址&#xff0c; 8 位控制段和 N 字节数据段。 如图所示…

行业分析:2023年藜麦市场竞争格局及发展现状分析

藜麦是藜科藜属植物。穗部可呈红、紫、黄&#xff0c;植株形状类似灰灰菜&#xff0c;成熟后穗部类似高粱穗。植株大小受环境及遗传因素影响较大&#xff0c;从0.3-3米不等&#xff0c;茎部质地较硬&#xff0c;可分枝可不分。单叶互生&#xff0c;叶片呈鸭掌状&#xff0c;叶缘…

科研小白必收藏,手把手教你写医学论文!

一篇完整的论文应包括标题、摘要、引言、方法、结果、讨论、致谢、参考文献八个部分&#xff0c;每个部分的写作都有技巧。 1.标题 标题一般采用名词词组或名词短语的形式&#xff0c;个别杂志也允许陈述句的形式。标题必须简单、明了&#xff0c;醒目。题目要新颖&#xff0c…

向库存抢利润!DigiOS微服务“库存中心”能力解读

作者&#xff1a;徐礼昭&#xff08;商派市场负责人&#xff0c;重构零售实验室负责人&#xff09; 同一件SKU&#xff0c;在不同渠道往往会出现“超卖”和“滞销”两种截然不同的情况。如何及时合理的调拨库存&#xff0c;实现产品的最大化销售&#xff08;降低库存成本&#…