JAVA基础:线程池的使用

news2024/12/26 2:56:00

目录

1.概述

2.线程池的优势​​​​​​​

2.1.线程池为什么使用自定义方式?

2.2.封装的线程池工具类有什么好处?

3.线程池的七大参数

3.线程池的创建

3.1. 固定数量的线程池

3.2. 带缓存的线程池

3.3. 执⾏定时任务

3.4. 定时任务单线程

3.5. 单线程线程池

3.6. 根据当前CPU⽣成线程池

3.7. ThreadPoolExecutor★★★

4.使用线程池的最佳实践

4.1.拥有适当数量的线程

4.2.使用合适的工作队列

4.3.处理异常

4.4.使用线程池内置的监控和调试工具

5.线程池的使用案例

5.1.引入jar包

5.2.初始化线程池

5.3.测试案例

5.结论

6.鸣谢


1.概述

线程池是一种常见的多线程编程技术,它允许我们在系统中使用一个固定数量的线程来执行任务,以免过多的线程拉低了系统的性能。在本文中,我们将探讨线程池的使用和一些最佳实践,以便在您的代码中获得更好的性能和可维护性。

线程池是一种用于管理和调度多个线程的技术。线程池主要由三个部分组成:

  • 线程管理器:负责启动、停止和管理线程池中的线程。
  • 工作队列:用于存储要执行的任务。
  • 线程池:包含线程管理器和工作队列。

线程池的工作原理如下:

1)当需要执行一个任务时,线程池会从工作队列中获取一个任务。

2)线程管理器会从线程池中获取一个可用的线程来执行任务。

3)任务执行完成后,线程会返回到线程池中,等待下一个任务的分配。

2.线程池的优势​​​​​​​

2.1.线程池为什么使用自定义方式?

因为 java 自带线程池都会有可能造成内存不足的问题。自定义线程池,根据服务器配置定制线程池核心线程、最大线程等,是最好的方式。

2.2.封装的线程池工具类有什么好处?

  • 扩展性高
  • 可注解形式实现执行
  • 可根据业务需要注册不同的线程池,区分业务模块使用
  • 可以执行无返回值线程任务,可以执行有返回值的线程任务

3.线程池的七大参数

核心线程数、最大线程数、多余线程存活时间、时间单位、线程工厂、阻塞队列、拒绝策略

 /**
     * @param corePoolSize 核心线程数 -> 线程池中保持的线程数量,即使它们是空闲的也不会销毁,
     *        除非设置了{@code allowCoreThreadTimeOut}核心线程超时时间
     * @param maximumPoolSize 最大线程数 -> 线程池中允许接收的最大线程数量
     *        如果设定的数量比系统支持的线程数还要大时,会抛出OOM(OutOfMemoryError)异常
     * @param keepAliveTime 最大存活时间 -> 当前线程数大于核心线程数的时候,
     *        其他多余的线程接收新任务之前的最大等待时间,超过时间没有新任务就会销毁.
     * @param unit {@code keepAliveTime}最大存活时间的单位.eg:TimeUnit.SECONDS
     * @param workQueue 工作队列 -> 保存任务直到任务被提交到线程池的线程中执行.
     * @param threadFactory 线程工厂 -> 当线程池需要创建线程得时候会从线程工厂获取新的实例.
     *        (自定义ThreadFactory可以跟踪线程池究竟何时创建了多少线程,也可以自定义线程的名称、
     *        组以及优先级等信息,甚至可以任性的将线程设置为守护线程.
     *        总之,自定义ThreadFactory可以更加自由的设置线程池中所有线程的状态。)
     * @param handler 当线程数量等于最大线程数并且工作队列已满的时候,再有新的任务添加进来就会进入这个handler,
     *        可以理解为设置拒绝策略(此处不清楚的可以看一下ThreadPoolExecutor中的execute方法的注释)
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
    }
参数说明
corePoolSize核心线程数量,线程池维护线程的最少数量
maximumPoolSize线程池维护线程的最大数量
keepAliveTime非核心线程的最长空闲时间,超过该时间的空闲线程会被销毁
unitkeepAliveTime的单位,有NANOSECONDS(纳秒)、MICROSECONDS(微秒)、MILLISECONDS(毫秒)、SECONDS(秒)
workQueue任务缓冲队列(阻塞队列)
threadFactory线程工厂,用于创建线程,一般用默认的即可
handler线程池对拒绝任务的拒绝策略

示例:创建一个核心线程数为5,最大线程数为10,任务队列容量为100的线程池

ThreadPoolExecutor的执行流程如下:

1、当线程池中新加入一个任务时,先判断核心线程数是否达到最大值,如果为false则创建一个核心线程执行任务,如果为true执行第二步;

2、判断当前任务队列是否已满,如果为false,则将任务加入到队列中等待执行;如果为true,则判断当前线程数是否达到最大线程数;

3、如果当前线程数没有达到最大线程数,则创建临时线程来执行任务,如果达到最大线程数,则执行拒绝策略。

拒绝策略指的是线程池中线程数量达到最大值,任务队列为满时,来了新任务的处理方式

 ThreadPoolExecutor提供了四种拒绝策略:

AbortPolicy:丢弃任务并抛出RejectedExecutionException异常(默认)

CallerRunsPolicy:由调用线程处理该任务( 常用)

DiscardPolicy:丢弃任务,但是不抛出异常。

DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务。

当然也可以自定义拒绝策略,只需要实现RejectedExecutionHandler接口即可

3.线程池的创建

线程池使用规范(阿里巴巴)

  1. 创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。
  2. 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。 自行创建线程,有可能造成系统创建大量同类线程而导致消耗完内存或者 “过度切换”的问题。
  3. 线程池不允许使用 Executors工厂类去创建(阿里的Java规范不推荐使用类Executors的静态方法创建线程池),而是通过new ThreadPoolExecutor 的方式,这样 的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

线程池的创建⽅法总共有 7 种,但总体来说可分为 2 类:

        1. 通过 ThreadPoolExecutor 创建的线程池;

        2. 通过 Executors 创建的线程池。

线程池的创建⽅式总共包含以下 7 种

(其中 6 种是通过 Executors 创建的, 1 种是通过ThreadPoolExecutor 创建)

        1. Executors.newFixedThreadPool:创建⼀个固定⼤⼩的线程池,可控制并发的线程数,超出的线程会在队列中等待;

        2. Executors.newCachedThreadPool:创建⼀个可缓存的线程池,若线程数超过处理所需,缓存⼀段时间后会回收,若线程数不够,则新建线程;

        3. Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执⾏顺序;

        4. Executors.newScheduledThreadPool:创建⼀个可以执⾏延迟任务的线程池;

        5. Executors.newSingleThreadScheduledExecutor:创建⼀个单线程的可以执⾏延迟任务的线程池;

        6. Executors.newWorkStealingPool:创建⼀个抢占式执⾏的线程池(任务执⾏顺序不确定)【JDK1.8 添加】。

        7. ThreadPoolExecutor:最原始的创建线程池的⽅式,它包含了 7 个参数可供设置,后⾯会详细讲。

3.1. 固定数量的线程池

线程池的使用(7种创建方法)_Youcan.的博客-CSDN博客

3.2. 带缓存的线程池

​​​​​​线程池的使用(7种创建方法)_Youcan.的博客-CSDN博客

3.3. 执⾏定时任务

线程池的使用(7种创建方法)_Youcan.的博客-CSDN博客

3.4. 定时任务单线程

线程池的使用(7种创建方法)_Youcan.的博客-CSDN博客

3.5. 单线程线程池

线程池的使用(7种创建方法)_Youcan.的博客-CSDN博客

为什么不直接用线程?单线程的线程池又什么意义?

        1. 复用线程。

        2. 单线程的线程池提供了任务队列和拒绝策略(当任务队列满了之后(Integer.MAX_VALUE),新来的任务就会拒绝策略)

3.6. 根据当前CPU⽣成线程池

线程池的使用(7种创建方法)_Youcan.的博客-CSDN博客

3.7. ThreadPoolExecutor★★★

线程池的使用(7种创建方法)

a. ThreadPoolExecutor 参数说明

b. 线程池执⾏流程

4.使用线程池的最佳实践

以下是使用线程池的一些最佳实践:

4.1.拥有适当数量的线程

线程池中的线程数量应该根据应用程序的需求来确定。通常情况下,线程池中的线程数量应该等于处理器数量加一。

4.2.使用合适的工作队列

线程池中的工作队列应该根据应用程序的需求来选择。如果需要执行大量的任务并且希望通过控制队列大小来限制系统的负载,则应该使用有界队列。如果希望系统能够保持高吞吐量并且不想限制队列大小,则应该使用无界队列

4.3.处理异常

线程池中的任务可能会抛出异常,因此我们应该在代码中处理这些异常。当任务抛出异常时,可以将异常记录到日志文件中或者通过线程池的回调函数进行处理。

4.4.使用线程池内置的监控和调试工具

许多线程池都提供了监控和调试工具,这些工具可以帮助我们诊断线程池中的问题。例如,线程池可以提供有关线程数量、队列大小和任务执行速度的统计信息。

5.线程池的使用案例

5.1.引入jar包

<!-- https://mvnrepository.com/artifact/concurrent/concurrent -->
<dependency>
    <groupId>concurrent</groupId>
    <artifactId>concurrent</artifactId>
    <version>1.3.4</version>
</dependency>
 <dependency>
     <groupId>com.google.guava</groupId>
     <artifactId>guava</artifactId>
     <version>23.0</version>
</dependency>

5.2.初始化线程池


import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @ClassName: AsyncScheduledTaskConfig
 * @author: dyt
 * @since: 2023/06/16 下午 4:58
 */
@Component
public class AsyncScheduledTaskConfig {

    @Bean
    public Executor myAsync() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //最大线程数
        executor.setMaxPoolSize(100);
        //核心线程数
        executor.setCorePoolSize(10);
        //任务队列的大小
        executor.setQueueCapacity(10);
        //线程前缀名
        executor.setThreadNamePrefix("dyt-thread-");
        //线程存活时间
        executor.setKeepAliveSeconds(30);

        /**
         * 拒绝处理策略
         * CallerRunsPolicy():交由调用方线程运行,比如 main 线程。
         * AbortPolicy():直接抛出异常。
         * DiscardPolicy():直接丢弃。
         * DiscardOldestPolicy():丢弃队列中最老的任务。
         */
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        //线程初始化
        executor.initialize();
        return executor;
    }
}

5.3.测试案例


import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @ClassName: ScheduleTask
 * @author: dyt
 * @since: 2023/06/19 下午 3:59
 */
@Component
@EnableAsync
public class ScheduleTask {

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Async("myAsync")
    @Scheduled(fixedRate = 2000)
    public void testScheduleTask() {
        try{
            Thread.sleep(6000);
            System.out.println("SpringBoot的定时任务" + Thread.currentThread().getName() + sdf.format(new Date()));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

运行

补充:
使用@Bean(“beanName”)定义线程池
然后在@Async(“beanName”)中引用指定的线程池 

5.结论

线程池是一种重要的多线程编程技术,可以帮助我们提高代码的性能和可维护性。通过了解线程池的最佳实践和使用经验,我们可以更好地使用线程池来构建高效的、稳定的多线程应用程序。

6.鸣谢

[1] https://blog.csdn.net/m0_48273471/article/details/124145012

[2] https://blog.csdn.net/YQQAGH178/article/details/119828128

[3] https://blog.csdn.net/qq_43681755/article/details/111057195

[4] https://blog.csdn.net/weixin_48410604/article/details/119386267

[5] https://blog.csdn.net/qq_42889280/article/details/123995250#t0

[6] https://blog.csdn.net/qq_24983911/article/details/94722569

[7] https://blog.csdn.net/qq_42889280/article/details/123995250#t0

[8] https://blog.csdn.net/weixin_37686415/article/details/112549576

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

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

相关文章

掌握唯米系统ChatGPT批量生成文章的操作技巧

以下是重写后的操作步骤&#xff1a; 1. 购买会员并添加个人的ChatGPT密钥&#xff1a; 首先&#xff0c;您需要购买唯米系统的会员&#xff0c;并获得访问ChatGPT的权限。随后&#xff0c;您可以将个人的ChatGPT密钥添加到系统中&#xff0c;以便使用该功能进行自然语言生成和…

Spring Session使用

一.使用场景 前后端不分离的情况下&#xff0c;往登陆页auth.gulimall.com的session中存放一个用户信息&#xff0c;想要在首页gulimall.com中取出该数据并展示出来 GetMapping("/oauth2.0/gitee/success")public String oauth2(RequestParam("code") Str…

组态王与PLC通过RJ45口建立无线以太网通讯

本文以组态王和2台三菱FX5u PLC为例&#xff0c;介绍组态王与多台 PLC的无线以太网通信实现过程。在本方案中采用了三菱PLC无线通讯终端DTD419MB&#xff0c;作为实现无线通讯的硬件设备。 在这一无线以太网通讯系统的搭建中&#xff0c;用户无需更改网络参数和原有程序&#…

Ubuntu的USB相关操作

这里写目录标题 0.信息查看1. 串口设备设置2. 串口调试助手 0.信息查看 指令lsusb输出Bus 004 Device 002: ID 05e3:0620 Genesys Logic, Inc. USB3.2 Hub Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 003 Device 006: ID 5986:115f Acer, Inc Integ…

佩戴最舒服的蓝牙耳机推荐,好用、佩戴体验很不错的蓝牙耳机分享

​面对市面上不同场景使用、不同类型的蓝牙耳机&#xff0c;我们选购蓝牙耳机时应该如何选&#xff1f;最怕遇到耳机延迟高、不防水防汗、音质表现差以及佩戴体验差的蓝牙耳机&#xff0c;针对这些经常面临的问题&#xff0c;我这次精选了四款市面上热销质量不错的蓝牙耳机分享…

PMP考试经验分享,准备不要超过三个月‼️

因为各种原因&#xff08;拖延、贪玩、上课 哈哈&#xff09; 我是用一个半月时间准备的PMP考试 3A通过 努力➕幸运的结果 资料准备&#xff1a; PMBOK第六版和第七版&#xff08;PMI官方教材&#xff09; 机构的视频材料&#xff08;巩固基础&#xff09; 模拟题库&#…

计算机原理基础一

内存 计算机数据存储&#xff0c;存储数据的基本单位是字节(Byte),常说的大b,1字节等于8位,8个bit(小b)。每个字节都对应一个内存地址&#xff0c;内存地址是从0开始编写的&#xff0c;然后自增排列&#xff0c;类似数组&#xff0c;C当中空类的大小是1个字节&#xff0c;就是…

JUC并发工具类--AQS

JUC并发工具类--AQS 管程 — Java同步的设计思想MESA模型 AQS&#xff08;AbstractQueuedSynchronizer&#xff1a;抽象队列同步器&#xff09;AQS简介AQS核心结构AQS内部维护属性state。state三种访问方式 两种资源访问方式AQS实现时主要实现的方法isHeldExclusively()tryAcqu…

安全应急产业新标兵, 前沿科技保安全

推广行业前沿技术 服务部队练兵备战 助力应急强国建设 科学应急 一馆一园一平台 安全应急项目推荐 海淀区安全应急产品展示交易中心 同赴时代之约&#xff0c;共创美好未来。 指导单位 2023年4月12日上午&#xff0c; “海淀区安全应急产品展示交易中心“揭牌启动仪式在中…

图像噪声和滤波

图像噪声 在图像采集、处理和传输过程中会受到噪声的影响。常见的图像噪声有椒盐噪声、高斯噪声等。 椒盐噪声 椒盐噪声也叫脉冲噪声&#xff0c;在图像中比较常见&#xff0c;表现为随机出现的噪点&#xff0c;比如在明亮区域中出现的黑色像素。参考下图的例子&#xff1a; 高…

【更改python版本】——日常记录

目录索引 卸载旧版本&#xff1a;提示&#xff1a;python launcher&#xff1a; 下载过程&#xff1a;配置pycharm&#xff1a;提示点&#xff1a;python console打不开的问题&#xff1a; 卸载旧版本&#xff1a; 要卸载 Python 3.7.7 在 Windows 上&#xff0c;请按照以下步骤…

接口测试持续集成

目录 一.在Windows系统上部署Jenkins1.下载Jenkins安装包2.启动Tomcat服务3.通过浏览器访问Jenkins 二.管理Jenkins中的插件1.安装插件2.更新插件3.卸载插件 三.创建项目四.配置项目运行频率 一.在Windows系统上部署Jenkins 1.下载Jenkins安装包 进入Jenkins官网&#xff0c;…

22-作用域链的理解

文章目录 作用域全局作用域函数作用域块级作用域二、词法作用域三、作用域链 作用域 &#x1f37f;&#x1f37f;&#x1f37f;作用域&#xff0c;即变量&#xff08;变量作用域又称上下文&#xff09;和函数生效&#xff08;能被访问&#xff09;的区域或集合 作用域决定了代…

【案例31】数据源密码保存不上

问题现象 客户反馈在启动BIP高级版时&#xff0c;Bip 2207启动异常。在相关的启动日志中排查发现&#xff0c;报数据源连接异常的错误。排查发现BIP高级版的数据源不通。发现密码字段为空导致。 问题分析 添加了正确的密码&#xff0c;测试通过保存。再次重启。发现还是报数据…

如何支持研发对CSDN个性化推荐系统重构

目录 大地图工具构建数据治理保持发布重视测试小结 一个以内容服务为主的软件&#xff0c;它的推荐系统在数据侧对软件产生着举足轻重的作用。数据的三个方面决定了这个内容软件的档次。 数据的质量好坏数据和用户需求的相关性好坏数据的层次体系好坏 通常&#xff0c;我们说…

40 # npm 的使用

npm 3n&#xff1a; nrm&#xff1a;node 中源管理工具nvm&#xff1a;node 中的版本管理工具npm&#xff1a;node 的包管理器&#xff0c;管理的都是 node 的模块 第三方模块 分两种&#xff1a; 全局模块&#xff1a;只能在命令行中使用&#xff0c;任何路径都可以本地模…

前端高频JS面试题(附答案+视频讲解)

高频前端js面试题总结 对应的视频讲解位置 2023前端高频面试题-JS高频面试题&#xff08;上&#xff09;_哔哩哔哩_bilibili 目录 1. var let const 的区别&#xff1f; 2. javascript 有哪些基础数据类型&#xff1f; 3. null和undefined区别 4. 与 的区别&#xff1f;…

二十三种设计模式第十二篇--组合模式

组合模式是一种结构型设计模式&#xff0c;它允许将对象组合成树形结构来表示整体-部分的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 在组合模式中&#xff0c;有两种类型的对象&#xff1a;叶子对象和组合对象。叶子对象表示树结构中的叶子节点&…

为什么需要建设海绵城市?

海绵城市&#xff0c;是新一代城市雨洪管理概念&#xff0c;是指城市在适应环境变化和应对雨水带来的自然灾害等方面具有良好的“弹性”&#xff0c;也可称之为“水弹性城市”。其本质上是一种生态化的城市发展模式&#xff0c;其核心是将城市建设与生态环境保护相结合。 为什么…

二.《UE4奥丁》解密哈希ID

哈希表概念 1.相信大家经常在UE4或者UE5游戏逆向中遇到下面的代码段 $ > > 41:8B42 0C > mov eax,dword ptr ds:[r10C] > $4 > 3B05 AE589B04 > cmp eax,dword ptr ds:[7FF7B68B74F4] …