Java自定义线程池

news2024/11/17 11:33:50

一、线程池的概念和作用

        线程池是一种用于管理和重用线程的机制。它允许你创建一个线程池,然后将任务提交给这个线程池,线程池会自动分配线程来执行这些任务。

        线程池的作用是优化线程的管理和资源利用,以减少线程创建和销毁的开销,提高系统的性能和响应速度。

二、线程池的参数

        如果是在 Spring 中,可以用 ThreadPoolTaskExecutor 配合 @Async 注解来实现。(不太建议)
        如果是在 Java 中,可以使用 JUC 并发编程包中的 ThreadPoolExecutor 来实现非常灵活地自定义线程池。

线程池参数:

public ThreadPoolExecutor(int corePoolSize, //核心线程数
                          int maximumPoolSize,  //最大工作线程数
                          long keepAliveTime,  //非核心线程存活时间
                          TimeUnit unit,   //时间单位
                          BlockingQueue<Runnable> workQueue,  //阻塞队列
                          ThreadFactory threadFactory,  //线程工厂
                          RejectedExecutionHandler handler //拒绝策略
) {

如何确定线程池的参数?

结合实际情况(实际业务场景和系统资源)来测试调整,不断优化。

corePoolSize(核心线程数):正常情况下,我们的系统应该能同时工作的线程数(随时就绪的状态)
maximumPoolSize(最大线程数 ):极限情况下,我们的线程池也最多有这么多的线程。
keepAliveTime(空闲线程存活时间):非核心线程在没有任务的情况下,过多久要删除(理解为开除临时工),从而释放无用的线程资源。
TimeUnit unit(空闲线程存活时间的单位):分钟、秒
workQueue(工作队列):用于存放给线程执行的任务,存在一个队列的长度(一定要设置,不要说队列长度无限,因为也会占用资源)
threadFactory(线程工厂):控制每个线程的生成、线程的属性(比如线程名)
RejectedExecutionHandler(拒绝策略):任务队列满的时候,我们采取什么措施,比如抛异常、不抛异常、自定义策略。

常见的拒绝策略:

  1. AbortPolicy(默认):这是默认的拒绝策略。当线程池的任务队列已满,且线程池中的线程数已达到最大值时,新任务将被立即拒绝,并抛出RejectedExecutionException异常。这是一种宁可抛出异常也不丢失任务的策略。

  2. CallerRunsPolicy:在这个策略下,当任务被拒绝时,会由提交任务的线程自己来执行这个任务。这意味着如果线程池被拒绝接受新任务,提交任务的线程将尝试执行该任务,从而减缓任务提交的速度。

  3. DiscardPolicy:这个策略下,新任务会被默默地丢弃,不会抛出异常,也不会被执行。这可能会导致任务的丢失。

  4. DiscardOldestPolicy:当任务被拒绝时,这个策略会丢弃任务队列中最老的任务,然后尝试再次提交新任务。这可以减少任务丢失的可能性,但可能会导致某些较旧的任务被丢弃。

  5. 自定义策略:除了上述内置策略,你还可以实现自定义的拒绝策略,以满足特定需求。你可以实现RejectedExecutionHandler接口,然后根据自己的逻辑来处理被拒绝的任务。

三、线程池如何工作?

假设核心线程数是:2

最大工作线程数是:4

任务队列大小为:5

下面分析线程池的工作过程:

(1)初始状态时,还没有任务的时候,线程池中是空的,没有线程。

(2)当来了一个任务时,任务数=1,此时就会创建一个核心线程来处理,核心线程数=1,

任务数=2时,再创建一个核心线程,核心线程数=2。此时核心线程数满了。

(3)此时如果再来一个任务,并不会创建线程来处理。而是放到任务队列中。

(4)当任务队列满了的时候,才会创建非核心线程来处理任务。如果线程数达到最大线程数,任务队列也满了,此时就会触发拒绝策略。

(5)如果当前线程数超过 corePoolSize(正式员工数),又没有新的任务给他,那么等 keepAliveTime 时间达到后,就可以把这个线程释放。

一般情况下,任务分为 IO 密集型和计算密集型两种。
计算密集型:吃 CPU,比如音视频处理、图像处理、数学计算等,一般是设置 corePoolSize 为 CPU 的核数 + 1(空余线程),可以让每个线程都能利用好 CPU 的每个核,而且线程之间不用频繁切换(减少打架、减少开销)
IO 密集型:吃带宽/内存/硬盘的读写资源,corePoolSize 可以设置大一点,一般经验值是 2n 左右,但是建议以 IO 的能力为主。

四、线程池的实现

在springboot项目中增加线程池配置类:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.validation.constraints.NotNull;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Configuration
public class ThreadPoolExecutorConfig {

    @Bean
    public ThreadPoolExecutor threadPoolExecutor() {

        //1. 创建线程工厂,用于为线程池创建新的线程对象
        ThreadFactory threadFactory = new ThreadFactory() {
            private int count = 1;

            @Override
            public Thread newThread(@NotNull Runnable r) {
                Thread thread = new Thread(r);
                thread.setName("线程" + count);
                count++;
                return thread;
            }
        };
        //2. 这段代码创建了一个名为 threadPoolExecutor的ThreadPoolExecutor 线程池实例,参数如下:
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 4, 100, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(4), threadFactory);
        return threadPoolExecutor;
    }
}

创建一个测试Controller:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.core.controller.BaseController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;


@RestController
@RequestMapping("/system/test")
public class QueueController extends BaseController {

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private ThreadPoolExecutor myThreadPoolExecutor;

    @PreAuthorize("@ss.hasPermi('system:user:list')")
    @GetMapping("/addTask")
    public void addTask(String taskName) {
        //这是Java 8引入的CompletableFuture类的方法,用于在异步线程中执行一个任务。
        // 这个方法接受一个Runnable对象作为参数,其中包含要执行的任务代码。在这个代码块中,任务会在后台线程中执行。
        CompletableFuture.runAsync(() -> {
            logger.info("任务执行中:"+taskName+",执行的线程为:"+ Thread.currentThread().getName());
            try{
                Thread.sleep(600000);
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        }, myThreadPoolExecutor); //传入线程池对象
    }

    @PreAuthorize("@ss.hasPermi('system:user:list')")
    @GetMapping("/get")
    public String get() {
        Map<String, Object> map = new HashMap<>();
        int queueSize = myThreadPoolExecutor.getQueue().size();
        map.put("队列长度",queueSize);

        long tasksNum = myThreadPoolExecutor.getTaskCount();
        map.put("任务总数",tasksNum);

        long completedTaskCount = myThreadPoolExecutor.getCompletedTaskCount();
        map.put("已完成的任务数",completedTaskCount);

        int activeCount = myThreadPoolExecutor.getActiveCount();
        map.put("正在工作的线程数", activeCount);

        // 使用Jackson库将map转换为JSON字符串
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(map);
            return json;
        } catch (Exception e) {
            e.printStackTrace();
            // 在异常情况下返回错误信息
            return "Error occurred: " + e.getMessage();
        }
    }
}

测试:

我们的线程池可以看到:

核心线程数:2

最大线程数:4

任务队列:4

添加1个任务:

查看线程池情况:

 再添加一个任务,此时任务数为2:

 再添加任务,此时任务数为3,因为核心线程数是2,所以会被放到任务队列:

 

继续添加任务,任务为6时,队列满了:

 

继续添加任务,会发现线程池会创建非核心线程来处理:

继续添加,当线程数为4时,队列长度也为4时,都满了: 

 

 如果继续添加任务,会触发拒绝策略:

后面当非核心线程没有工作时,超过过期时间,就会被线程池销毁掉。 

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

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

相关文章

竞赛选题 深度学习交通车辆流量分析 - 目标检测与跟踪 - python opencv

文章目录 0 前言1 课题背景2 实现效果3 DeepSORT车辆跟踪3.1 Deep SORT多目标跟踪算法3.2 算法流程 4 YOLOV5算法4.1 网络架构图4.2 输入端4.3 基准网络4.4 Neck网络4.5 Head输出层 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; *…

保姆级教程,教你AI数字人应该怎么制作?!

随着人工智能的发展&#xff0c;利用数字人制作短视频已经成为当下火热的项目&#xff0c;因为这种方式不需要真人出镜&#xff0c;避免了个人不上镜或者不喜上镜而不能做短视频的缺点&#xff0c;用数字人代替真人&#xff0c;不仅内容里人物有了&#xff0c;而且这种形式还非…

智能井盖是什么?万宾科技智能井盖传感器有什么特点

智能井盖是一种基于物联网和人工智能技术的新型城市设施。它不仅具备传统井盖的功能&#xff0c;还能通过数字化、自动化的方式实现远程监控和智能管理&#xff0c;提升城市运行效率和服务水平。 WITBEE万宾智能井盖传感器EN100-C2是一款井盖异动监测的传感终端。对窨井盖状态(…

shein面试:nacos无入侵配置,做过吗,怎么做?

说在前面 在40岁老架构师 尼恩的读者社区(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如阿里、滴滴、极兔、有赞、shein 希音、百度、网易的面试资格&#xff0c;遇到很多很重要的面试题&#xff1a; 无入侵配置&#xff0c;做过吗&#xff0c;怎么做的&#xff1f;Na…

剪映怎么一键去水印?三分钟教会你

剪映怎么一键去水印&#xff1f;无论是使用剪映提供的方法还是专业的去水印软件&#xff0c;都能够帮助用户轻松去除视频中的水印&#xff0c;提高视频的质量和观赏性&#xff0c;以根据自己的需求和场景选择合适的方法进行操作&#xff0c;今天就教大家如何使用剪映一键去水印…

美光科技发布1β制程节点技术的16Gb DDR5存储器,领先业界 | 百能云芯

存储器大厂美光科技&#xff08;Micron&#xff09;宣布推出采用 1β 制程节点技术的 16Gb DDR5 存储器。美光 1β DDR5 DRAM 的内置系统功能速率可达 7200MT/s&#xff0c;目前已出货给所有资料中心及 PC 端客户。美光 1β DDR5 存储器采用先进高介电常数 CMOS 制程、四相位时…

Failed to process, please exclude the tableName or statementId.

说明&#xff1a;执行一次查询时&#xff0c;报下面这个错误&#xff1b; Failed to process, please exclude the tableName or statementId.排查结果&#xff0c;在Mapper.xml里面&#xff0c;对应的statement使用了复杂的函数&#xff0c; <select id"getLastEleRa…

ODrive移植keil(八)—— 闭环控制

目录 一、硬件接线二、官方代码操作2.1、力矩模式2.2、速度模式2.3、位置模式 三、移植后的代码操作3.1、力矩模式3.2、速度模式3.3、位置模式3.4、跳过上电校准3.4.1、手动输入参数3.4.2、flash保存参数 3.5、测试云台电机 四、代码说明五、定点运算和浮点运算 ODrive、VESC和…

全感知智能配电房:让电力运行可控、高效

在当今数字化、智能化的时代&#xff0c;全感知智能配电房的出现无疑为电力行业带来了革命性的变革。这种新型配电房不仅提高了电力供应的效率&#xff0c;还大大降低了运营成本&#xff0c;为我们的日常生活和工作提供了更稳定、更可靠的电力保障。 力安科技全感知智能配电…

Teleport

从官网中获取到的代码如下 App.vue <template><div class"outer"><h3>Tooltips with Vue 3 Teleport</h3><div><MyModal /></div></div> </template> <script setup> import MyModal from "./My…

pinia踩坑之旅——在组件外使用pinia

pinia踩坑之旅——在组件外使用pinia 缘由 最近在使用 pinia 开发项目时产生了一个 bug&#xff0c;说在定义 pinia 前使用了 pinia。 报错如下&#xff1a; 代码展示 先来看一个我的代码&#xff08;这里我新开了一个项目用于演示&#xff09;&#xff0c;如果懒得看代码的…

【Python基础】数值类型

int(整形) 在 Python 中定义变量是 不需要指定类型&#xff08;在其他很多高级语言中都需要&#xff09; 整形&#xff0c;也被称之为整数。整数就是数学中的数字。 整形在Python中不受长度限制大小范围 使用 type 函数可以查看一个变量的类型 In[1]: 1 Out[1]: 1In[2]: t…

Win10修改编辑hosts文件无法保存的处理方法

1.首先打开hosts文件所在位置&#xff0c;我们输入C:WindowsSystem32Driversetc后回车就可以打开了&#xff0c;右键hosts文件&#xff0c;选择属性。 2.点击hosts属性对话框里的“高级”。 3.在hosts的高级安全设置界面点击更改权限&#xff0c;在新弹出的对话框里点击添加按…

TStor CSP文件存储在大模型训练中的实践

业务背景 大模型作为人工智能领域的重要发展趋势&#xff0c;正在逐渐改变人们的生活和工作方式。随着近年来大模型领域技术的突破&#xff0c;各类语言模型、图像模型、视频模型快速演进&#xff0c;国内外市场也不断涌现出优秀的大模型研究及商业化平台&#xff0c;预期通过…

竞赛 深度学习人体语义分割在弹幕防遮挡上的实现 - python

文章目录 1 前言1 课题背景2 技术原理和方法2.1基本原理2.2 技术选型和方法 3 实例分割4 实现效果5 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习人体语义分割在弹幕防遮挡上的应用 该项目较为新颖&#xff0c;适合作为竞…

GoogleNet论文精读

论文名&#xff1a;Going depper with convolutions论文下载地址&#xff1a;https://github.com/jixiuy/paper引言第一段&#xff1a;背景成绩1*1的卷积在channel上升维和降维&#xff0c;channel融合&#xff0c;计算方法上等价于FNNGAP&#xff08;全局平均池化&#xff09;…

协同云办公原来可以这么简单!只需掌握这5个技巧

随着云计算技术的发展&#xff0c;协同云办公已经成为越来越多企业和团队的必备工具。但是&#xff0c;对于很多人来说&#xff0c;如何高效地进行协同云办公却仍是一个挑战。本文将介绍五个简单的技巧&#xff0c;让你轻松掌握协同云办公的秘诀&#xff0c;让你的工作更高效、…

《潮玩产业发展报告(2023)》发布 泡泡玛特进军海外潮玩市场

近期&#xff0c;新华网联合中国社会科学院财经战略研究院发布了《超越潮流&#xff1a;千亿级潮玩产业彰显人文经济价值——潮玩产业发展报告&#xff08;2023&#xff09;》&#xff08;下称《报告》&#xff09;。针对潮玩产业快速发展&#xff0c;课题组组长、中国社会科学…

25台兰博基尼跑车赛道巡游!泡泡玛特MOLLY攒的局就是这么拉风

入秋以来气温逐渐转冷&#xff0c;但泡泡玛特的市场活动却持续升温&#xff1a;国内首个潮玩行业沉浸式IP主题乐园泡泡玛特城市乐园正式开园&#xff1b;2023PTS上海国际潮流玩具展&#xff1b;入驻美国第二大商场、布里斯班再拓新店等海外布局步伐不停……将广大消费者的身心带…

C++数据结构X篇_18_二叉树的创建(根据遍历结果创建二叉树;#号法创建树)

本篇将会介绍二叉树的创建&#xff0c;重点学习#号法创建树的方法。 文章目录 1. 根据遍历结果创建二叉树&#xff08;只需记住结论即可&#xff09;1.1 首先有一个问题&#xff0c;根据中序遍历的结果能确定一棵树吗&#xff1f;1.2 那如何才能确定一棵树&#xff1f;&#x…