线程池,及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,一经查实,立即删除!

相关文章

基于模块化的智能变电站电气系统

摘要 本次设计是我们的毕业设计,本次设计的变电站的类型为地区变电站&#xff0c;是为了满足市区生产和生活的要求&#xff0c;根据老师给出的设计资料和要求&#xff0c;结合所学的基础知识和文献资料所做的。通过本设计&#xff0c;对以前所学的知识加强了理解和掌握&#x…

第19章 正则表达式 - C++

第19.1节 介绍 正则表达式库提供表示正则表达式的类&#xff0c;正则表达式是一种用于在字符串中匹配模式的微型语言。下列数种对象上的操作能刻画几乎所有带正则表达式的操作&#xff1a; 目标序列。为模式而搜索到的字符序列。这可以是二个迭代器所指定的范围、空终止字符串…

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

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

Java实现微信小程序支付前后端

微信小程序支付的前后端实现可以分为以下几个步骤&#xff1a; 微信开放平台注册账号并创建小程序应用。 在小程序后台设置支付相关信息&#xff0c;包括支付证书、支付回调地址等。 在小程序前端页面中调用wx.requestPayment函数&#xff0c;调起微信支付界面。 在小程序后…

GB/T 35379-2017 木门分类和通用技术要求

木门是指主要采用实木、人造板或其他木质复合材料制成的门框和门扇&#xff0c;根据表面材料的不同分为实木门&#xff0c;实木复合门和木质复合门&#xff0c;按照功能的不同分为防火木门、防盗木门、防潮木门、隔声木门等。 GB/T 35379-2017 木门检测项目 测试项目 测试标准…

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

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

提高你的 Python 编程技巧:10个实用技巧

在日常的 Python 编程工作中&#xff0c;掌握一些实用的技巧可以提高你的效率和代码质量。本文将介绍 10 个实用的 Python 技巧&#xff0c;帮助你更好地利用 Python 进行开发。 1. enumerate 在循环中&#xff0c;有时我们需要同时获取元素的索引和值。这时可以使用 enumera…

用 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;这里进行一下扩展学…

LoadBalancer将服务暴露到外部实现负载均衡metallb-layer2模式配置介绍

目录 一.metallb简介 1.支持多种负载均衡协议 2.支持自定义 IP 地址范围 3.无需额外的硬件设备 4.易于安装和配置 5.可扩展性强 6.layer2模式下选举的leader节点压力大 二.layer2模式配置演示 1.开启ipvs并开启严格ARP模式 2.下载并应用metallb 3.创建一个 IPAddres…

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

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

2023年甘肃省职业院校技能大赛(中职教师组)网络安全竞赛样题(四)

2023年甘肃省职业院校技能大赛&#xff08;中职教师组&#xff09; 网络安全竞赛样题&#xff08;四&#xff09; &#xff08;总分1000分&#xff09; 目录 模块A 基础设施设置与安全加固 A-1任务一 登录安全加固&#xff08;Windows&#xff09; A-2任务二 本地安全策略…

搭建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…

【开题报告】基于SpringBoot的文学鉴赏平台的设计与实现

1.选题背景 随着互联网的普及和文学教育的重视&#xff0c;人们对文学作品的阅读和交流需求逐渐增加。传统的纸质书籍已经不能完全满足人们多样化的阅读需求&#xff0c;因此基于互联网的文学鉴赏平台应运而生。这样的平台通过提供文学作品的展示、评论和交流功能&#xff0c;…

ARM64安全特性之SMAP

ARM64 SMAP&#xff08;Supervisor Mode Access Prevention&#xff09;是一种安全扩展&#xff0c;用于在ARM64架构中限制特权模式下的内存访问。它的目的是防止特权模式下的恶意软件或漏洞利用程序访问受限内存区域&#xff0c;从而提高系统的安全性。 SMAP的主要原理是通过…

new Promise用法

promise要解决的问题 回调函数问题 问题一&#xff1a;回调函数多层嵌套调用&#xff08;回调地狱&#xff09; 问题二&#xff1a;每次回调的结果存在成功或失败的可能性 使用 promise 解决 解决问题一&#xff1a;promise 通过 .then 实现链式调用 解决问题二&#xff1a;p…

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…