线程池,及7大参数,4大拒绝策略详解

线程池,及7大参数,4大拒绝策略详解

1. 前言

1.1 什么是线程池?

线程池是一种利用池化技术思想来实现的线程管理技术,主要是为了复用线程、便利地管理线程和任务、并将线程的创建和任务的执行解耦开来。我们可以创建线程池来复用已经创建的线程来降低频繁创建和销毁线程所带来的资源消耗。在JAVA中主要是使用ThreadPoolExecutor类来创建线程池,并且JDK中也提供了Executors工厂类来创建线程池(不推荐使用)。

线程池的优点:
降低资源消耗,复用已创建的线程来降低创建和销毁线程的消耗。
提高响应速度,任务到达时,可以不需要等待线程的创建立即执行。
提高线程的可管理性,使用线程池能够统一的分配、调优和监控。

1.2 为什么使用线程池?

没有线程池时

image-20231205201638662

从上面可以看出之前显示的创建线程的一些缺点:

1)不受控制风险,对于每个创建的线程没有统一管理的地方,每个线程创建后我们不知道线程的去向。
2)每执行一个任务都需要创建新的线程来执行,创建线程对系统来说开销很高

而若是用线程池来管理线程

image-20231205201739050

执行相同任务时可以复用线程并不用频繁创建和销毁。

线程池的好处:

降低资源的消耗
提高响应的速度
方便管理
线程复用、控制最大并发数、管理线程

强制----线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。

说明:线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。

如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。

2.JAVA线程池概述

Java中线程池的核心实现类是ThreadPoolExecutor,可以通过该类地构造方法来构造一个线程池,我们先来看下ThreadPoolExecutor的整个继承体系

image-20231205202330250

Executor接口

  • java.util.concurrent.Executor 接口提供了一种将任务的提交与任务的执行分离的机制。它包含一个单一的方法 execute(Runnable command),用于执行传递的任务。

ExecutorService接口

  • java.util.concurrent.ExecutorService 接口扩展了Executor接口,提供了更丰富的功能来管理线程池。

AbstractExecutorService类

  • AbstractExecutorService 类是 ExecutorService 接口的一个抽象实现,提供了一些默认实现和辅助方法,使得实现自定义的线程池变得更加容易。该类实现了部分 ExecutorService 接口中的方法,留下了一些抽象方法需要具体的子类来实现。

ThreadPoolExecutor类

  • java.util.concurrent.ThreadPoolExecutorExecutorService接口的一个实现,它提供了一个灵活且可扩展的线程池实现。
  • 通过 ThreadPoolExecutor 的构造方法可以创建一个线程池,配置核心线程数、最大线程数、线程空闲时间、工作队列等参数,以满足不同场景的需求。

2.1七大参数

ThreadPoolExecutor类提供了七个参数,这些参数用于配置线程池的行为。

  1. corePoolSize(核心线程数)
    • 线程池的基本大小,即在没有任务需要执行时,线程池的大小是多少。
    • 如果调用了prestartAllCoreThreads()方法,线程池会在启动时提前创建并启动所有的核心线程。
  2. maximumPoolSize(最大线程数)
    • 线程池允许创建的最大线程数。如果队列满了,并且活动线程数小于最大线程数,线程池会创建新的线程来执行任务。
    • 如果使用了无界队列(例如LinkedBlockingQueue),则该参数就不起作用。
  3. keepAliveTime(空闲线程存活时间)
    • 当线程池中的线程数量大于核心线程数时,多余的空闲线程在终止之前等待新任务的最长时间。超过这个时间就会被回收。
    • 如果设置了allowCoreThreadTimeOut为true,则核心线程也会超时终止。
  4. unit(空闲线程存活时间的单位)
    • keepAliveTime 参数的时间单位,通常是 TimeUnit.SECONDS 或 TimeUnit.MILLISECONDS。
  5. workQueue(阻塞队列)
    • 用于保存等待执行的任务的阻塞队列。可以选择不同类型的队列,例如 LinkedBlockingQueue、ArrayBlockingQueue 等。
    • 这是线程池的关键参数之一,不同的队列类型会影响线程池的行为。
  6. threadFactory(线程工厂)
    • 用于创建新线程的工厂。可以通过实现 ThreadFactory 接口来自定义线程的创建过程,例如为线程指定名称、优先级等。
  7. handler(拒绝策略)
    • 当工作队列满并且线程池中的线程数达到最大线程数时,用于处理新提交的任务的策略。

简单使用线程

public class Main {public static void main(String[] args) {int corePoolSize = 4; //核心线程数int maximumPoolSize = 8; //最大线程数long keepAliveTime = 10000; //空闲线程存活时间TimeUnit unit = TimeUnit.MILLISECONDS; //空闲线程存活时间的单位BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(8); //阻塞队列ThreadFactory threadFactory = new ThreadFactory() {  //线程工厂@Overridepublic Thread newThread(Runnable r) {System.out.println("创建线程:" + r);return new Thread(r);}};RejectedExecutionHandler handler = null;//拒绝策略ThreadPoolExecutor myThreadPool = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,new CustomerThreadPool.MyAbortPolicy());for (int i = 0; i < 16; i++) {myThreadPool.execute(() -> {// 执行的线程for (int j = 0; j < 10; j++) {System.out.println(Thread.currentThread() + ":执行" + j + "次");}});}myThreadPool.shutdown();}
}

image-20231205205947025

2.2四大拒绝策略

Java中的线程池在任务提交过程中,如果线程池已满且无法继续创建新线程,就会触发拒绝策略。Java提供了四种预定义的拒绝策略,它们分别是:

  1. AbortPolicy(中止策略)

    • 默认的拒绝策略,会直接抛出 RejectedExecutionException 异常,阻止系统正常运行。
  2. CallerRunsPolicy(调用者运行策略)

    • 将任务回退给调用线程来执行。在这个策略中,任务提交者会自己去执行该任务,从而降低新任务的提交速度,以适应系统的处理能力。
  3. DiscardPolicy(丢弃策略)

    • 直接丢弃新的任务,没有任何处理。如果系统对任务丢失不敏感,可以使用这个策略。
  4. DiscardOldestPolicy(丢弃最老策略)

    • 丢弃队列中最老的任务,然后尝试重新提交当前任务。这样可以腾出队列空间来接收新的任务,但可能会丢失一些等待执行的任务。

这些拒绝策略提供了不同的处理方式,可以根据实际场景和需求选择合适的策略。通过 setRejectedExecutionHandler 方法,可以在创建 ThreadPoolExecutor 实例时指定拒绝策略。

2.3测试四大拒绝策略

  1. AbortPolicy(中止策略)

    public class Main {public static void main(String[] args) {// 创建线程池ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 2, 1,TimeUnit.MINUTES, new LinkedBlockingDeque<>(1), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());// 创建4个任务for(int i = 0; i < 4; i++) {pool.execute(() -> System.out.println(Thread.currentThread().getName() + " ... "));}// 关闭线程池pool.shutdown();}
    }
    

    执行结果:
    image-20231205213042860

    分析:

    最大的线程数是2,阻塞队列中可以存一个,最多就可接收3个任务,当有4个任务时,就会执行拒绝策略,此拒绝策略为丢弃任务,抛出异常

  2. CallerRunsPolicy(调用者运行策略)

    public class Main {public static void main(String[] args) {// 创建线程池ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 2, 1,TimeUnit.MINUTES, new LinkedBlockingDeque<>(1), Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());// 创建4个任务for(int i = 0; i < 4; i++) {pool.execute(() -> System.out.println(Thread.currentThread().getName() + " ... "));}// 关闭线程池pool.shutdown();}
    }
    

    执行结果:

    image-20231205213210604

    分析:

    最大的线程数是2,阻塞队列中可以存一个,最多就可接收3个任务,当有4个任务时,就会执行拒绝策略,因为是主线程创建的线程,所以当任务没有线程执行的时候,会由创建此线程的线程来执行,也就是由主线程来执行,主线程名为main

  3. DiscardPolicy(丢弃策略)

    public class Main {public static void main(String[] args) {// 创建线程池ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 2, 1,TimeUnit.MINUTES, new LinkedBlockingDeque<>(1), Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardPolicy());// 创建4个任务for(int i = 0; i < 4; i++) {pool.execute(() -> System.out.println(Thread.currentThread().getName() + " ... "));}// 关闭线程池pool.shutdown();}
    }
    

    执行结果:

    image-20231205213420782

    分析:

    最大的线程数是2,阻塞队列中可以存一个,最多就可接收3个任务,当有4个任务时,就会执行拒绝策略,丢弃新产生的任务

  4. DiscardOldestPolicy(丢弃最老策略)

    public class Main {public static void main(String[] args) {// 创建线程池ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 2, 1,TimeUnit.MINUTES, new LinkedBlockingDeque<>(1), Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardOldestPolicy());// 创建4个任务for(int i = 0; i < 4; i++) {pool.execute(() -> System.out.println(Thread.currentThread().getName() + " ... "));}// 关闭线程池pool.shutdown();}
    }
    

    执行结果:

    image-20231205213557566

    分析:

    有2线程执行,第3个任务超出了最大线程数,所以进入阻塞队列,第4个任务发现了阻塞队列满了,此时最大线程数已经达到最大值了,而阻塞队列也满了,所以执行拒绝策略:丢弃阻塞队列中老任务(也就是第3个任务),将新的任务(第4个任务)添加进去,最后等前面任务执行完,释放线程后,执行了阻塞队列中的任务,也就是任务4被执行了,所以共执行了3个任务,丢弃了一个(任务3)

总结

阿里巴巴编码规范对于线程池的使用有一些规范和建议,以下是一些主要的指导原则:

  1. 线程池基本规范
    • 推荐使用线程池的方式创建线程:使用线程池可以减少线程的创建和销毁开销,提高系统的性能。
    • 使用ThreadPoolExecutor创建线程池:尽量使用ThreadPoolExecutor类创建线程池,以便灵活地配置线程池参数。
  2. 线程池参数配置
    • 避免使用无界队列:无界队列(如LinkedBlockingQueue)可能导致队列无限增长,最终耗尽系统资源。建议使用有界队列来限制队列的长度。
    • 合理配置线程池大小:根据业务场景和系统资源,合理配置核心线程数、最大线程数、存活时间等参数,以优化线程池的性能。
    • 避免使用固定大小的线程池:固定大小的线程池可能在高并发时无法处理大量的请求,建议根据实际需求使用可伸缩的线程池。
  3. 拒绝策略选择
    • 慎重选择拒绝策略:根据实际业务场景,选择合适的拒绝策略。通常情况下,建议使用CallerRunsPolicy,避免直接抛出异常或丢弃任务。
    • 定制拒绝策略:如果默认的拒绝策略无法满足需求,可以实现自定义的拒绝策略。
  4. 避免线程池滥用
    • 谨慎使用线程池:线程池不是万能的,不适合所有场景。在某些特定的业务场景中,可能需要考虑使用其他并发控制手段,如信号量、CountDownLatch 等。
  5. 任务提交方式
    • 使用executesubmit合理execute适用于不需要获取执行结果的场景,而submit适用于需要获取执行结果的场景。
  6. 处理异常
    • 及时处理任务中的异常:对于submit方法提交的任务,需要通过Future.get()来检查任务执行结果,包括异常。如果不及时处理异常,可能导致任务失败而无法及时发现问题。
  • 谨慎使用线程池:线程池不是万能的,不适合所有场景。在某些特定的业务场景中,可能需要考虑使用其他并发控制手段,如信号量、CountDownLatch 等。
  1. 任务提交方式
    • 使用executesubmit合理execute适用于不需要获取执行结果的场景,而submit适用于需要获取执行结果的场景。
  2. 处理异常
    • 及时处理任务中的异常:对于submit方法提交的任务,需要通过Future.get()来检查任务执行结果,包括异常。如果不及时处理异常,可能导致任务失败而无法及时发现问题。

这些规范和建议有助于确保线程池的稳定运行,提高系统的性能和可维护性。在具体应用时,还需根据具体业务场景和需求进行适度调整。

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

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

相关文章

【4】密评-网络和通信安全测评

0x01 依据 GB/T 39786 -2021《 信息安全技术 信息系统密码应用基本要求》针对等保三级系统要求&#xff1a; 网络和通信层面&#xff1a; a&#xff09;应采用密码技术对通信实体进行身份鉴别&#xff0c;保证通信实体身份的真实性&#xff1b; b&#xff09;宜采用密码…

【数电笔记】07-基本和复合逻辑运算

目录 说明&#xff1a; 基本逻辑运算 1. 与运算 &#xff08;and gate&#xff09; 2. 或运算 &#xff08;or gate&#xff09; 3. 非运算 &#xff08;not gate &#xff09; 复合逻辑运算 1. 与非运算&#xff08;nand&#xff09; 2. 或非运算&#xff08;nor&…

用 taichi 写个软渲染器

用 taichi 写个软渲染器 What 起点是&#xff1a;可以 setup 一个画布&#xff0c;drawPixel(x, y, color)&#xff0c;然后渲染到 GUI 或者 .png目标是&#xff1a;加载 obj 模型文件和 .tga 贴图文件&#xff0c;并渲染出来使用 taichi 作为 SIMD 加速 backend复现一些 RTR…

leetcode:统计感冒序列的数目【数学题:组合数含逆元模版】

1. 题目截图 2.题目分析 需要把其分为多个段进行填充 长为k的段&#xff0c;从两端往中间填充的方案数有2 ** (k - 1)种 组合数就是选哪几个数填哪几个段即可 3.组合数含逆元模版 MOD 1_000_000_007 MX 100_000# 组合数模板 fac [0] * MX fac[0] 1 for i in range(1, MX…

伯俊软件CTO陈雨陆:R3全渠道业务中台的OceanBase落地实践

11 月 16 日&#xff0c;OceanBase 在京顺利举办 2023 年度发布会&#xff0c;正式宣布&#xff1a;将持续践行“一体化”产品战略&#xff0c;为关键业务负载打造一体化数据库。其中&#xff0c;“数字化转型升级实践专场”我们有幸邀请到伯俊软件 CTO 陈雨陆进行《OceanBase …

从Intel Cyclone10GX TransceiverPHY 高速收发器认识ATX PLL、FPLL、CMU PLL等PLL

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 在使用Intel Cyclone10GX TransceiverPHY的过程中发现这个IP还是比较复杂的&#xff0c;特别是时钟系统&#xff0c;提到了多种PLL:ATX PLL、FPLL、CMU PLL&#xff0c;这里进行一下扩展学…

用友U8 Cloud RegisterServlet SQL注入漏洞复现

0x01 产品简介 用友U8 Cloud是用友推出的新一代云ERP,主要聚焦成长型、创新型企业,提供企业级云ERP整体解决方案。 0x02 漏洞概述 用友U8 Cloud RegisterServlet接口处存在SQL注入漏洞,未授权的攻击者可通过此漏洞获取数据库权限,从而盗取用户数据,造成用户信息泄露。 …

搭建React项目,基于Vite+React+TS+ESLint+Prettier+Husky+Commitlint

基于ViteReactTSESLintPrettierHuskyCommitlint搭建React项目 node: 20.10.0 一、创建项目 安装包管理器pnpm npm i pnpm -g基于Vite创建项目 pnpm create vitelatest web-gis-react --template react-ts进入项目目录安装依赖 $ cd web-gis-react $ pnpm i启动项目 $ pnpm…

Python的海龟 turtle 库使用详细介绍(画任意多边形,全网最详细)

学Turtle库&#xff0c;其实就是学数学&#xff0c;而且还能提高对数学和学习的兴趣。Turtle库还能够帮助孩子更好地理解几何学和数学概念&#xff0c;比如角度、比例、几何图形的性质等等&#xff0c;是Python中一个很有趣的库。 前言 Turtle库是Python中一个很有趣的库&…

Web开发-问题-前后端交互数据不一致

0x01 问题描述 所用的技术&#xff1a;VueSpring Boot后端传给前端数据&#xff1a; [Student(studentId1, personorg.fatmansoft.teach.models.Person4abe6020, major软件工程, className一班, grade一年级), Student(studentId2, personorg.fatmansoft.teach.models.Person…

百元开放式蓝牙耳机哪款好、热门高性价比开放式推荐

在众多耳机类型中&#xff0c;开放式耳机正逐渐崭露头角。它们融合了音质和佩戴舒适性&#xff0c;能给你带来全新的佩戴感受。这些耳机不仅提供高品质的音响体验&#xff0c;还让你能够在户外佩戴欣赏音乐的同时保持对周围环境的感知&#xff0c;更加安全、保障。 在本文中&a…

IDEA版SSM入门到实战(Maven+MyBatis+Spring+SpringMVC) -Mybatis核心配置详解

第一章 Mybatis核心配置详解【mybatis-config.xml】 1.1 核心配置文件概述 MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 1.2 核心配置文件根标签 没有实际语义&#xff0c;主要作用&#xff1a;所有子标签均需要设置在跟标签内部 1.3 核心配置文件…

Lambda表达式与方法引用

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 引子 先来看一个案例 …

RPC基础

RPC基础知识 RPC 是什么? RPC&#xff08;Remote Procedure Call&#xff09; 即远程过程调用&#xff0c;通过名字我们就能看出 RPC 关注的是远程调用而非本地调用。 为什么要 RPC &#xff1f; 因为&#xff0c;两个不同的服务器上的服务提供的方法不在一个内存空间&…

Hadoop学习笔记(HDP)-Part.07 安装MySQL

目录 Part.01 关于HDP Part.02 核心组件原理 Part.03 资源规划 Part.04 基础环境配置 Part.05 Yum源配置 Part.06 安装OracleJDK Part.07 安装MySQL Part.08 部署Ambari集群 Part.09 安装OpenLDAP Part.10 创建集群 Part.11 安装Kerberos Part.12 安装HDFS Part.13 安装Ranger …

无人机停机坪的主要功能有哪些

随着无人机行业领域的不断完善&#xff0c;无人机停机坪作为一项关键基础设施&#xff0c;正发挥着越来越重要的作用。无人机停机坪也叫无人机机巢、无人机机库、无人机机场。无人机停机坪不仅是无人机的“家”&#xff0c;更是其高效运行的关键环节。让我们一同探索无人机停机…

探讨电能质量监测与治理解决方案在半导体行业的设计与应用-安科瑞 蒋静

摘要&#xff1a;在国家鼓励半导体材料国产化的政策导向下&#xff0c;本土半导体材料厂商不断提升半导体产品技术水平和研发能力&#xff0c;逐渐打破了国外半导体厂商的垄断格局&#xff0c;推进中国半导体材料国产化进程。半导体产品的制造使用到的设备如单晶炉、多晶炉等都…

Isaac Sim教程01 Isaac Sim介绍

Isaac Sim 介绍 版权信息 Copyright 2023 Herman YeAuromix. All rights reserved.This course and all of its associated content, including but not limited to text, images, videos, and any other materials, are protected by copyright law. The author holds all…

【MySQL】基本安装配置

1 基础知识 1.1 MySQL安装 下载地址&#xff1a;https://dev.mysql.com/downloads/mysql/ 1.1.1 安装过程 配置环境变量&#xff08;和配置Java8的环境变量如出一辙&#xff09;在MySQL解压文件夹下&#xff0c;导入my.ini文件&#xff0c;与bin目录同级&#xff0c;具体文…

七、Linux服务器集群搭建

详见《Linux服务器集群搭建》 【往期回顾】 一、Linux系统概述和安装 二、Linux基础命令 三、Linux高级命令 四、虚拟机网络配置 五、Linux基础软件安装 六、shell编程