线程池详解(建议收藏)

news2024/9/24 19:20:01

概念

线程池(Thread Pool)是一种基于池化技术的多线程处理形式,用于管理线程的创建和生命周期,以及提供一个用于并行执行任务的线程队列。线程池的主要目的是减少在创建和销毁线程时所花费的开销和资源,提高程序性能,同时也提供了对并发执行任务的更好管理,例如控制线程数量。

使用线程池的好处

  • 线程复用:线程池中的线程可以被重复利用,用于执行多个任务,避免了频繁创建和销毁线程的性能开销。提高响应速度
  • 资源控制:线程池可以限制系统中线程的最大数量,防止因为线程数过多而消耗过多内存,或者导致过高的上下文切换开销。
  • 更方便的管理:通过线程池提供了可配置的参数,如核心线程数、最大线程数、空闲线程存活时间、任务队列的大小等,允许定制以适应不同的应用需求

线程池七大参数详解

在这里插入图片描述

核心线程数

这是线程池中始终保持的线程数量,即使它们处于空闲状态也不会被回收,这样保证当新任务到来时能够快速进行响应

核心线程数的计算要参考系统中占比大部分时间的稳定流量来计算,因为核心线程数如果设置的过大可能会造成线程资源的浪费,比如系统的流量平均为100QPS,每个请求执行时间为0.2秒,那么核心线程数设置为100 * 0.2 = 20个

而针对请求高峰期的特殊情况,我们可能通过设置最大线程数来应对,这使得线程资源能够被最高兴的利用

任务队列

线程池中的任务队列是一个非常重要的组件,它用于存储等待被线程池中的线程执行的任务,当所有核心线程都忙于处理任务,那么新任务将被放入任务队列中排队等待,只有当任务队列已满时,线程池才会尝试创建新的非核心线程来处理任务,直到线程池中的线程数量达到最大线程数

任务队列的大小,可以设计为核心线程每秒所能处理的任务数的 2倍即可,比如我们上面计算出了核心线程数为20,每秒可以处理100个请求,那么任务队列的大小设置为200即可

常见任务队列

SynchronousQueue
它与其他阻塞队列不同的地方在于,它内部并不持有任何数据元素,也就是说它没有任何内部容量(容量为0)。SynchronousQueue 的工作方式更像是一个传递手棒的机制,它适用于将任务直接从生产者传递给消费者的场景。

例如我们可以这么定义线程池

private final ExecutorService executor = new ThreadPoolExecutor(0, 200,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy());

比如我系统中有一个定时任务,执行时间是每晚凌晨1点到3点,这个时候我核心线程设为0,阻塞队列为0,最大线程设为200,,这样的好处是

  • 核心线程数为0,意味着定制任务非执行时间不会占用任何线程资源,避免浪费
  • 定时任务开始执行后,每来一个任务就会创建一个线程,可以快速创建线程响应请求,且线程的最大空闲时间被设置为60秒,意味着在定时任务执行期间这些非核心线程也不会被频繁的创建销毁

LinkedBlockingQueue
LinkedBlockingQueue是有容量的,可以用作阻塞队列,默认容量是Integer.MAX_VALUE,可看做是无穷大的容量,这意味着无论多大的并发量都会去排队执行,不会触发拒绝策略

private static final ExecutorService executor = new ThreadPoolExecutor(32,
            32, 60000L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),
            new CommonThreadFactory(),
            new ThreadPoolExecutor.DiscardOldestPolicy());

也可以通过构造器参数指定队列容量

最大线程数

这是线程池可以同时运行的最大线程数量。当工作队列满了,且当前运行的线程数少于最大线程数时,线程池会创建新的线程来处理任务。需要注意,最大线程数中是包括核心线程数的,最大线程数的比如遇到双11、618这种请求高峰期,可能QPS会达到500,根据这个计算得出,最大线程数的计算公式 = (系统最大请求任务数 - 任务队列任务数)

拒绝策略

AbortPolicy

这个策略会直接抛出一个RejectedExecutionException异常,从而拒绝新任务的执行。这是默认的策略,这种策略也算是丢弃了任务任务,只不过相比DiscardPolicy,AbortPolicy通过抛出异常显示的拒绝任务,这样在任务调用的地方,开发者可以通过异常捕获来做日志打印、利用递归重新提交任务等操作,但需要注意,高并发情况下可能同一个任务再重试的时候会频繁触发拒绝策略导致不断重试,这样的话会造成严重性能损耗,所以采取两个策略①等待重试,即让线程sleep一段时间再进行重试②限制重试次数,案例代码如下

public class RetryTaskExample {

    public static void main(String[] args) {
        // 创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2, // 核心线程数
                4, // 最大线程数
                10, // 空闲线程的存活时间
                TimeUnit.SECONDS, // 时间单位
                new ArrayBlockingQueue<>(2), // 任务队列
                new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
        );

        // 提交任务
        for (int i = 1; i <= 6; i++) {
            final int taskId = i;
            submitTask(executor, taskId, 0);
        }

        // 关闭线程池
        executor.shutdown();
    }

    private static void submitTask(ThreadPoolExecutor executor, int taskId, int count) {
        try {
            executor.execute(() -> {
                System.out.println("Running task " + taskId + " by " + Thread.currentThread().getName());
                try {
                    // 模拟任务执行时间
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    System.out.println("Task " + taskId + " was interrupted.");
                }
            });
            System.out.println("Successfully submitted task " + taskId);
        } catch (RejectedExecutionException e) {
            // 日志记录
            System.out.println("Task " + taskId + " rejected: " + e.getMessage());
            System.out.println("Trying to resubmit task " + taskId);
            
            // 简单重试策略
            try {
                if (count < 3) {
                    // 等待一段时间后重试
                    TimeUnit.MILLISECONDS.sleep(500);
                    submitTask(executor, taskId, ++count); // 递归调用,重新提交任务
                }

            } catch (InterruptedException ex) {
                System.out.println("Retry of task " + taskId + " was interrupted.");
            }
        }
    }
}

DiscardPolicy

相比于这个策略将悄无声息地丢弃无法处理的任务,不会抛出异常,也不会给调用者任何提示。

CallerRunsPolicy

将任务回退给调用者线程(即提交任务的线程)来直接执行,这种方式会降低对线程池的新任务提交速度,因为提交任务的线程被占用来执行任务,但可以保证无论何种情况任务都会被提交并执行,虽然不是默认的策略,但确实实际开发中用的最多的策略。案例代码

    public static void main(String[] args) {
        // 创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2, // 核心线程数
                4, // 最大线程数
                10, // 空闲线程的存活时间
                TimeUnit.SECONDS, // 时间单位
                new ArrayBlockingQueue<>(2), // 任务队列
                new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
        );

        Long startTime = System.currentTimeMillis();
        // 通过for循环来模拟高并发的场景
        // 主线程通过调用方法submitTask来提交任务,线程池中的线程来执行任务,但在并发量过大,超出了线程池的
        for (int i = 1; i <= 10; i++) {
            final int taskId = i;
            submitTask(executor, taskId, 0);
        }

        // 关闭线程池
        executor.shutdown();
    }

日志打印为
在这里插入图片描述
由此可知,第1,2个任务由核心线程去执行,第3,4个线程存储在任务队列中,第5,6个线程由非核心线程去处理,第7,8个线程就会由main线程去处理,只有等main线程执行完毕后才会去提交并执行剩余的任务

DiscardOldestPolicy

这个策略将尝试丢弃队列中最早的一个任务,然后尝试再次提交新任务(不保证成功)。这种方式在任务可以被丢弃且希望运行最新任务时可能会有用。

线程池的应用

框架中的应用

Web服务器

在处理HTTP请求时,每个请求都可以作为一个独立的任务提交到线程池中,由线程池中的线程处理,这样做的好处是可以快速响应用户请求,同时复用线程资源

开发中的常见常见

异步任务处理

例如发送电子邮件、执行后台计算等这些都可以作为异步任务提交给线程池,从而不会阻塞主程序的执行。

并行数据处理

在处理大数据集时,可以将数据分割成小块,并行地在多个线程上进行处理,例如

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

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

相关文章

C# messagePack对类(class)序列化简单示例

c# messagepack对类&#xff08;class&#xff09;序列化的简单示例 0 引言1 示例2 纠错3 结语 0 引言 MessagePack是一种高效的二进制序列化格式&#xff0c;它可以在C#中用于序列化和反序列化对象。与其他序列化格式相比&#xff0c;如JSON和XML&#xff0c;MessagePack的编…

西安电子高速PCB学习(五)

感抗&#xff08;Inductive Reactance&#xff09;和容抗&#xff08;Capacitive Reactance&#xff09;是电感和电容在交流电路中对电流产生阻碍的特性。这两个概念源于交流电路中&#xff0c;电感和电容对交流电流的相应反应。 感抗&#xff08;Inductive Reactance&#xf…

如何使用ssm实现电脑配件销售系统的设计与实现

TOC ssm128电脑配件销售系统的设计与实现jsp 第一章 绪 论 1.1背景及意义 系统管理也都将通过计算机进行整体智能化操作&#xff0c;对于电脑配件销售系统所牵扯的管理及数据保存都是非常多的&#xff0c;例如管理员&#xff1b;主页、个人中心、用户管理、商品分类管理、商…

PyTorch升级之旅——主要组成模块

本文仅作为个人学习记录使用 文章目录 前言 一、深度学习的简单流程 二、基本配置 三、数据读入 四、模型构建 五、模型初始化 六、损失函数 七、训练和评估 八、可视化 九、PyTorch优化器 总结 前言 学习链接&#xff1a;第三章&#xff1a;PyTorch的主要组成模块…

推荐几个缓解工作心情的VS Code插件

vs-cats 使用了这个插件&#xff0c;在写到"cat"/"Cat"单词时&#xff0c;可以在单词左边出现猫猫表情&#xff0c;而且在鼠标停靠在单词附近时还能显示猫猫图片 vscode-pets 使用这个插件可以在VS Code中养很多电子小动物&#xff0c;可以饲养的宠物有&a…

跟《经济学人》学英文:2024年08月24日这期 How to attract Indian tourists

How to attract Indian tourists Destinations are competing for the travelling rupee 原文&#xff1a; INDIANS ARE on the move. In 2019 international departures from India hit 27m, a number that will surely be exceeded this year and is predicted to rise t…

2024年好用的4款电脑录屏工具清单。

如果你需要录制教学视频、操作演示、记录游戏等等&#xff0c;但不知道使用哪些软件可以实现&#xff0c;那你一定要看看这个文章。因为我帮助大家筛选了4款能够用于电脑屏幕录制的高效工具。 1、福昕电脑REC 直达&#xff1a;www.foxitsoftware.cn/REC/ 这个软件是很多必备的…

电商api接口进行数据采集获取多平台商品价格

在电商运营中&#xff0c;从品牌角度来看&#xff0c;品牌方通过接口进行数据采集&#xff0c;获取多渠道商品价格信息的这一行为&#xff0c;能为品牌方带来诸多好处&#xff1a; 及时准确&#xff1a;API接口能为品牌提供实时数据&#xff0c;这意味着企业可以即时获取最新的…

Transformer总结(三):组件介绍(位置编码,多头注意,残差连接,层归一化,基于位置的前馈网络)

文章目录 一、位置编码1.1 介绍1.2 简单探讨 二、多头注意力2.1 一般的多头注意力机制2.2 解码器中的掩码多头注意力机制&#xff08;Look-ahead Mask&#xff09; 三、残差连接四、层归一化4.1 对比不同的Normalization4.2 Batch Normalization的实现4.3 Layer Normalization的…

vue3使用simple-mind-map,分分钟开发思维导图

这几天又来了新需求&#xff0c;老板想在系统里一眼可以看到所有部门的相关信息&#xff0c;并且可以编辑&#xff0c;分配任务。所以需要实现一个可编辑的思维导图页面。 思维导图&#xff1f;感觉很复杂的样子&#xff0c;这种很牛p的东西应该不是我三两天就能手写搞定的&am…

I2C代码硬件实现

环境 芯片:STM32F103ZET6 库&#xff1a;来自HAL的STM32F1XX.H 原理图 有图可知SCL和SDA两条线接到了PB10和PB11 芯片的复用功能是I2C2 代码 Driver_I2C.h #include "Driver_I2C.h"void Driver_I2C_Init(void) {/* 1. 打开引脚和片上外设的时钟 I2C2 PB10 PB11 …

【提示学习论文】AAPL: Adding Attributes to Prompt Learning for Vision-Language Models

AAPL: Adding Attributes to Prompt Learning for Vision-Language Models&#xff08;2024CVPR&#xff09; 问题&#xff1a;在unseen class上&#xff0c;性能提升有限解决&#xff1a;在提示学习中引入对抗性标记嵌入adversarial token embedding&#xff0c; 将低层次视觉…

SOMEIP_ETS_060: SD_Discover_Port_and_IP

测试目的&#xff1a; 验证设备&#xff08;DUT&#xff09;能够响应测试器发出的多播FindService消息&#xff0c;并返回一个单播OfferService消息&#xff0c;列出所有必要的IP地址和端口&#xff0c;以满足与DUT的所有可能通信需求。 描述 本测试用例旨在检查DUT是否能够…

清理linux的buff/cache缓存

通过free -m命令&#xff0c;查看内存占用率。 如果buff/cache 占用内存过高的话&#xff0c;执行以下命令 sync && echo 1 > /proc/sys/vm/drop_caches sync && echo 2 > /proc/sys/vm/drop_caches sync && echo 3 > /proc/sys/vm/drop_ca…

文字怎么生成二维码扫码展示?文本活码在线生成的3步操作技巧

现在很多人会将文本信息用生成二维码的方式来展现&#xff0c;通过二维码来分享内容能够有效的提升内容的传播效率&#xff0c;简化其他人获取信息的流程。只需要通过扫码就能在手机上预览内容&#xff0c;更加符合现在人们的生活习惯&#xff0c;而且这种方式可以降低制作者的…

bedtools安装与使用(v2.31.1)生物信息学工具29

01 背景 bedtools&#xff1a;一个用于基因组算术的强大工具集 总体而言&#xff0c;bedtools 工具集是一个多功能的工具包&#xff0c;可用于广泛的基因组分析任务。其中最广泛使用的工具能够进行基因组算术运算&#xff0c;即在基因组上执行集合论操作。例如&#xff0c;be…

前端实现投影坐标和地理坐标系(CGCS2000)转换

前言 地理坐标系和投影坐标系是地理信息系统&#xff08;GIS&#xff09;和制图学中用于描述地球表面位置的两种不同的参考系统。它们在使用方式和应用场景上有显著的区别。 1、什么是地理坐标系和投影坐标系&#xff1f; 1.1、地理坐标系 地理坐标系是基于地球的三维球面模…

虚拟机安装centos7-桥接模式

1、打开虚拟机&#xff0c;点击文件&#xff0c;选择新建虚拟机 2、选择典型&#xff0c;点击下一步 3、选择稍后安装操作系统&#xff0c;点击下一步 4、选择系统类型及版本&#xff0c;点击下一步&#xff0c;因centos7是Linux操作系统&#xff0c;且是64位的&#xff0c;所以…

隐藏的艺术:滥用 404 页面的新 Magecart 活动

介绍 一项新的、复杂的、隐蔽的 Magecart 网页窃取活动已针对 Magento 和 WooCommerce 网站展开。该活动的部分受害者与食品和零售行业的大型组织有关。 根据我们发现的证据,该活动已经持续了几周,在某些情况下甚至更长。该活动采用了一种我们以前从未遇到过的高级隐藏技术…

Linux 内核源码分析---组播/策略路由选择

在 Linux 实现中&#xff0c;组播路由选择不像单播路由选择那样可以由内核单独处理&#xff0c;组播守护程序种类繁多&#xff0c;如 mrouted 和 pimd&#xff0c;它们分别基于距离矢量组播路由选择协议&#xff08;Distance Vector Multicast Routing Protocol&#xff0c;DVM…