JAVA基础:线程池的使用

2023-11-04

目录

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 非核心线程的最长空闲时间,超过该时间的空闲线程会被销毁
unit keepAliveTime的单位,有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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JAVA基础:线程池的使用 的相关文章

随机推荐

  • 基于图像的火焰识别调研总结

    转载请注明出处https blog csdn net Hey chaoxia article details 85054870 简单总结一下近期的调研结果 一 简介 火焰的图像识别 主要围绕火焰的颜色特征 运动特征 几何特征与纹理特征来分析
  • Vue3+Vite+Pinia+Naive后台管理系统搭建之一:基础项目构建

    前言 如果对 vue3 的语法不熟悉的 可以移步 Vue3 0 基础入门 快速入门 github 开源库 Vue3 Vite Pinia Naive Js gitee 开源库 Vue3 Vite Pinia Naive Js 1 构建项目
  • AQS流程图

    整个AQS加锁解锁过程
  • jq 获取下拉框的值和value

    误区 一直以为jquery获取select中option被选中的文本值 是这样写的 s text 获取所有option的文本值 实际上应该这样 s option selected text 获取选中的option的文本值 获取select中
  • 深入了解ln命令:创建硬链接和符号链接的实用指南

    文章目录 1 引言 1 1 关于ln命令 1 2 ln命令的作用和用途 2 基本用法 2 1 创建硬链接 2 2 创建符号链接 2 3 区别硬链接和符号链接 3 操作示例 3 1 创建硬链接的示例 3 2 创建符号链接的示例 3 3 查看链
  • eval()函数的用法

    1 eval 函数函数基本原理 eval s 函数将去掉字符串s最外侧的引号 并按照Python语句方式执行去掉引号后的字符内容 使用方式如下 lt 变量 gt eval lt 字符串 gt a eval 1 2 print a 输出结果
  • 客户管理系统 详细流程(不用三大框架)

    客户管理系统的详细编写流程 一 系统设计 1 需求分析 系统中包含哪些数据模型 数据模型存在怎样的关系 E R图 UML 用例图 2 制作页面Demo 和真实系统效果一样 给客户确认需求 3 技术选型 环境搭建 软件建模工具 IBM RSA
  • [Python-9]GUI编程

    一 GUI图形用户界面编程介绍 我们前面实现的都是基于控制台的程序 程序和用户的交互通过控制台来完成 本章 我们将学习GUI Graphics User Interface 即图形用户界面编程 我们可以通过python提供的丰富的组件 快速
  • SSM商城项目实战:后台管理用户认证

    SSM商城项目实战 后台管理用户认证 1 项目概述 本项目是一个基于SSM Spring SpringMVC MyBatis 框架的商城后台管理系统 用于管理商品 订单和用户等信息 在本篇博客中 我们将重点介绍如何实现后台管理用户的认证功能
  • opengl点的绘制

    include stdafx h include
  • unity的错误解决办法:NullReferenceException: Object reference not set to an instance of an object;tiny proje

    普通unity项目 正常的项目中 这是个非常初级的错误 比较常见的原因是就是在对象被引用前没有实例化 即是说 你要管理好程序的生命周期 解决办法1 设置不同的生命周期 生命周期就是脚本中的Awake Start Update等方法 他们会以
  • WIN 10环境下安装pytorch环境并跑通Yolox-demo

    文章目录 前言 一 Pytorch是什么 二 搭建Pytorch框架步骤 1 安装Anaconda 2 安装CUDA和CUDNN 3 安装Pytorch 三 Pycharm上跑通Yolox 1 下载Yolox项目 2 导入pycharm并设
  • 使用IDEA快速部署到Docker云端

    一 Docker对外提供服务 1 编辑服务配置文件 vim lib systemd system docker service 2 定位到ExecStart开头行 将原本的ExecStart usr bin dockerd H fd con
  • js aes加密_威feng网站的aes算法破解

    网站是 aHR0cHM6Ly93d3cuZmVuZy5jb20v 话说这个网站在过年前使用了aes算法 当然过年后也是aes 但就是把秘钥换了 换成更需要解密一段字符串 然后获得秘钥 最后请求时候再去用这个秘钥加密 并且最后发现秘钥和偏移是
  • mysql语句添加字段和注释

    简单点的 工作中需要对某个表进行添加字段 输出脚本 添加字段关键字 alter 注释关键字 comment ALTER TABLE 表名 ADD 字段名 varchar 20 COMMENT 注释内容 不要注释 去掉comment就行了 o
  • python opencv图片二值化后取出图片中心区域的轮廓

    python opencv图片二值化后取出图片中心区域的轮廓 1 导入必要的库 import cv2 import numpy as np 2 读取图片并将其转为灰度图像 image cv2 imread image jpg gray cv
  • vue封装指针样式

    新建style js文件 HandPointer 鼠标经过 BodyPointer 默认指针 可以设置多个样式 const HandPointer gt return cursor url require 图片路径 auto const B
  • Antd中RangePicker组件弹出的日历中英混杂,月份和周显示为英问题

    在开发中需要用到antd中的RangePicker组件 但是出现了一个问题 日历中英混杂 年份是中文 月份和周都是英文 搜索了一下这个问题主要是moment造成的 可能有很多种情况 需要根据项目的自身情况来修改 首先确定一下项目中packa
  • 计算机视觉基础(十)—— HOG特征描述算子之行人检测

    本次任务将学习一种在深度学习之前非常流行的图像特征提取技术 方向梯度直方图 Histogram of Oriented Gradients 简称HOG特征 HOG特征是在2005年CVPR的会议发表 在图像手工特征提取方面具有里程碑式的意义
  • JAVA基础:线程池的使用

    目录 1 概述 2 线程池的优势 2 1 线程池为什么使用自定义方式 2 2 封装的线程池工具类有什么好处 3 线程池的七大参数 3 线程池的创建 3 1 固定数量的线程池 3 2 带缓存的线程池 3 3 执 定时任务 3 4 定时任务单线