Java多款线程池,总有一款适合你。

news2024/11/26 23:22:10

线程池的选择

  • 一:故事背景
  • 二:线程池原理
    • 2.1 ThreadPoolExecutor的构造方法的七个参数
      • 2.1.1 必须参数
      • 2.1.2 可选参数
    • 2.2 ThreadPoolExecutor的策略
    • 2.3 线程池主要任务处理流程
    • 2.4 ThreadPoolExecutor 如何做到线程复用
  • 三:四种常见线程池
    • 3.1 newCachedThreadPool
    • 3.2 newFixedThreadPool
    • 3.3 newSingleThreadExecutor
    • 3.4 newScheduledThreadPool
  • 四:线程池如何实现参数的动态修改
  • 五:实际应用
  • 六:总结提升

一:故事背景

最近咋搞多线程的研究学习。本文会系统的告诉你,Java中的多种线程池,以及在项目中该如何去选择对应的线程池,提升项目的处理能力。
池化技术是程序设计中非常常见的一种思想,大家可以通过我的博客池化思想了解,什么是池化思想。
为什么我们要使用线程池而不是创建线程去执行任务呢?

  1. 复用已创建的线程,避免创建线程的时候耗费资源
  2. 对线程进行统一管理
  3. 控制并发的数量,不至于创建的线程过多,导致资源消耗过多,最终造成服务器崩溃。

二:线程池原理

想要了解线程池原理,就先从其类图开始
在这里插入图片描述
Executor 是线程池的顶级接口,其定义了方法execute。其中ThreadPoolExecutor类是我们重点关注的类,让我们来看看其构造方法

2.1 ThreadPoolExecutor的构造方法的七个参数

ThreadPoolExecutor一共有七个参数,其中5个是必须的参数,2个值可选参数。

2.1.1 必须参数

  1. int corePoolSize:该线程池中核心线程数最大值
    核心线程会一直存在在线程池中,无论是否有需要待执行的任务

  2. int maximumPoolSize:该线程池中线程总数最大值 。
    该值等于核心线程数量 + 非核心线程数量。这两个值加起来决定了可以创建多少个非核心线程

  3. long keepAliveTime:非核心线程闲置超时时长。非核心线程如果处于闲置状态超过该值,就会被销毁。如果设置allowCoreThreadTimeOut(true),则会也作用于核心线程。

  4. TimeUnit unit:keepAliveTime的单位。TimeUnit是一个枚举类型 ,包括以下属性:
    NANOSECONDS : 1微毫秒 = 1微秒 / 1000 MICROSECONDS : 1微秒 = 1毫秒 / 1000 MILLISECONDS : 1毫秒 = 1秒 /1000 SECONDS : 秒 MINUTES : 分 HOURS : 小时 DAYS : 天

  5. BlockingQueue workQueue:阻塞队列,维护着等待执行的Runnable任务对象。
    常用的几个阻塞队列:
    LinkedBlockingQueue :链式阻塞队列
    ArrayBlockingQueue:数组阻塞队列
    SynchronousQueue:同步队列,内部容量为0,每个put操作必须等待一个take操作,反之亦然。
    DelayQueue:延迟队列,该队列中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素 。

2.1.2 可选参数

  1. ThreadFactory threadFactory 创建线程的工厂,用于批量创建线程,可以在创建线程的时候指定一些参数,列如 守护线程,线程优先级等等。
  2. RejectedExecutionHandler handler。如果阻塞队列满了,且线程数大于最大线程数的时候就会采用拒绝策略。拒绝策略一共有四种:
  • ThreadPoolExecutor.AbortPolicy:默认拒绝处理策略,丢弃任务并抛出RejectedExecutionException异常。

  • ThreadPoolExecutor.DiscardPolicy:丢弃新来的任务,但是不抛出异常。

  • ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列头部(最旧的)的任务,然后重新尝试执行程序(如果再次失败,重复此过程)。

  • ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

这四种拒绝策略是我们设计线程池必须要考虑的部分。在使用时,要估计可能产生的并发数,会不会触发拒绝策略,如果触发了拒绝策略我们该如何处理,如何保证程序提供功能的完整性。例如针对默认拒绝处理策略,我们可以捕获对应异常,进行重试。或者选择丢弃任务,然后过一段时间批量执行未成功的任务。

2.2 ThreadPoolExecutor的策略

线程池本身也有一个调度线程,这个线程用于管理布控整个线程池的任务和事务。线程池也有自己的状态。在ThreadPoolExecutor内使用了一些final int常量变量表示了线程池的状态。
在这里插入图片描述

  • 线程池创建后就处于RUNNING状态
  • 调用shutdown()方法后处于SHUTDOWN状态,线程池不能接受新的任务,清除空闲的worker,不会等待阻塞队列任务完成
  • 调用shutdownNow()方法后处于STOP状态,线程池不能接受新的任务,中断所有线程,阻塞队列中没有被执行的任务全部丢弃。此时,poolsize=0,阻塞队列的size也为0。
  • 当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。接着会执行terminated()函数。
  • 线程池处在TIDYING状态时,执行完terminated()方法之后,就会由 TIDYING -> TERMINATED, 线程池被设置为TERMINATED状态。

2.3 线程池主要任务处理流程

线程池主要任务处理流程在其execute方法内体现,让我们看看其是如何处理线程任务的:
在这里插入图片描述

2.4 ThreadPoolExecutor 如何做到线程复用

上文我们说到线程池可以用来复用已经创建的线程对象,那么它到底是怎么做的呢?
其实,ThreadPoolExecutor在创建线程的时候会将线程封装成工作线程 worker、然后放入工作线程组中,然后反复的从阻塞队列中去拿任务去执行。
addWorker方法:
在这里插入图片描述
worker对象循环去阻塞队列获取任务:
在这里插入图片描述
获得任务之后,不断的进行task.run 执行对应的任务。
在getTask中,如果是在核心线程上的话,任务将会卡在workQueue.take();方法上,线程不会结束,如果是非核心线程的话,非核心线程会workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) ,如果超时还没有拿到,下一次循环判断compareAndDecrementWorkerCount就会返回null,Worker对象的run()方法循环体的判断为null,任务结束,然后线程被系统回收 。
在这里插入图片描述

三:四种常见线程池

上文我们已经讲到了ThreadPoolExecutor线程池,讲解了其内部的各个参数以及各个参数如何选择。Execuors提供了几个不同的静态方法进行线程池的创建,这几个线程池底层都是使用的ThreadPoolExecutor进行的实现。

3.1 newCachedThreadPool

在这里插入图片描述
newCachedThreadPool适合执行很多的短时间的任务,并且线程60s会进行回收,占用资源不多。此线程池的任务会先将任务添加到synchronousQueue队列。由于线程池很大,几乎不会触发拒绝策略。

3.2 newFixedThreadPool

在这里插入图片描述
newFixedThreadPool只创建核心线程,不创建非核心线程。就算没有任务,核心线程也会保存。而且由于LinkedBlockingQueue的默认大小是Integer.MAX_VALUE,几乎不会触发拒绝策略。

3.3 newSingleThreadExecutor

在这里插入图片描述
只创建一个核心线程处理任务,如果这个核心线程不空闲,新来的任务就放入阻塞队列,所有的任务按照先来先执行的顺序进行。

3.4 newScheduledThreadPool

在这里插入图片描述

这四种常见的线程池,基本就够用了,但是如果业务规模过大,则存在资源耗尽的风险,所以还时老老实实的使用ThreadPoolExecutor类,自己进行参数配置吧。

四:线程池如何实现参数的动态修改

由于系统的复杂性,我们往往可能需要动态的调成线程池的参数,ThreadPoolExecutor类提供了几个方法来进行线程池参数的设置:
在这里插入图片描述
主要有两个思路进行设置:
1:利用Nacos,业务服务读取线程池的配置,获取相对应的线程池实例进行线程池参数的修改。
2:也可以扩展ThreadPoolExecutor,重写方法,监听线程池参数的个变化,动态的修改线程池的参数。

五:实际应用

我们的项目场景中,年终总结的部分,用到了多线程,批量的计算用户本年度的各种参与数据,计算好之后,放入数据库中,用户直接读取即可。参数是如下选择:

  • corePoolSize:线程核⼼参数选择了0
  • maximumPoolSize:最⼤线程数选择了cpu*2
  • keepAliveTime:⾮核⼼闲置线程存活时间直接置为60
  • unit:⾮核⼼线程保持存活的时间选择了 TimeUnit.SECONDS 秒
  • workQueue:线程池等待队列,使⽤ LinkedBlockingQueue阻塞队列

由于我们是通过job任务进行触发,选择的是晚上用户少的时候进行的执行,并且只有晚上才会进行一次计算,所以并不需要保留核心线程占用程序客供件,只需要在任务处理时增加计算效率即可。

六:总结提升

多线程无疑会提升我们程序的效率,但是其参数选择非常重要,必须要结合线程池清楚ThreadPoolExecutor的7个参数,合理选择参数才能够安全使用。希望本篇文章能增加你对多线程的理解。

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

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

相关文章

企业数字化转型与股利分配(2007-2021年)

参照李滟(2023)的做法,本团队对来自西南大学学报(社会科学版)《企业数字化转型与股利分配》一文中的基准回归部分进行复刻。 企业数字化转型已成为我国经济增长的新引擎和新动力。为探究数字化转型对企业财务决策的影…

Spring 依赖注入和自动装配

DI(依赖注入) DI:Dependency Injection 共有三种方式 构造器注入 在前面IOC容器创建对象的方式中已经提到,无参构造器和有参构造器都可以。 Set方式注入(重点) 依赖注入:本质是Set注入 依赖…

【Linux】高级IO

目录 IO的基本概念 钓鱼五人组 五种IO模型 高级IO重要概念 同步通信 VS 异步通信 阻塞 VS 非阻塞 其他高级IO 阻塞IO 非阻塞IO IO的基本概念 什么是IO? I/O(input/output)也就是输入和输出,在著名的冯诺依曼体系结构当中…

LinuxC编程——进程间通信(二)(信号、共享内存)

目录 一、信号1.1 概念1.2 信号的响应方式⭐⭐⭐1.3 几种常见的信号1.4 函数练习 二、共享内存2.1 共享内存的特点2.2 共享内存创建步骤⭐⭐2.3 共享内存创建所需函数 信号主要用来通知进程异步事件的发生。最初信号设计的目的是为了处理错误,它们也用来作为最基本的…

【EI/SCOPUS检索】第二届能源与动力工程国际学术会议(EPE 2023)

第二届能源与动力工程国际学术会议(EPE 2023) 2023 2nd International Conference on Energy and Power Engineering 能源是人类社会发展的重要推动力量。如何安全、清洁、高效地存储、转化和利用能源,实现人类可持续发展,一直…

比例方向阀控制多功能放大器

适用于控制无电位置反馈的三位四通比例方向阀,两路独立工作的比例放大器,可组合成并联工作方式,0到10V输入接口,可切换为0(4)到20mA输入,工作电压24VDC,允许工作温度范围0~45℃,放大器只有在断电…

spring security实践-全套代码

贴一套完整代码 电脑文件都被加密了,无法上传git,留一套在此,日后方便。 整个学习过程参考的spring security 1. 项目目录结构 2.初始化数据库 CREATE TABLE sys_user (id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 主键,user_name VAR…

你确定你的 REST API 真的符合 REST 规范?

RESTful API 的存在是 web 开发历史上的一个里程碑。在本文中,我将和你探讨几种节省 REST API 开发时间的方法,并给出相关的 Node.js 示例。 什么是 RESTful API 首先,想问一个问题,你的项目里真的有真正的 RESTful API 吗&…

【构造】CF1734 E

Problem - 1734E - Codeforces 题意&#xff1a; 思路&#xff1a; Code&#xff1a; #include <bits/stdc.h>#define int long longusing i64 long long;constexpr int N 1e3 10; constexpr int mod 1e9 7;int n; int a[N], ans[N][N];void solve() {std::cin &g…

动力节点零基础Java学习教程,从入门到进阶,轻松掌握Java开发技能

Java是一门应用非常广泛的编程语言&#xff0c;对于零基础自学Java来说&#xff0c;Java的学习过程可能会有一些困难&#xff0c;但只要掌握了相关的基础知识和技能&#xff0c;不断地实践和总结&#xff0c;就能真正掌握Java编程。 动力节点老杜的Java经典之作&#xff0c;哔站…

Jenkins构建自由风格项目发布jar到服务器

前面的文章有介绍 docker安装jenkins 和 dockerjenkins发布spring项目&#xff1b;这里就不做过多的介绍&#xff0c;直接说明构建步骤。 1、选择构建一个自由风格的项目 2、 选择丢弃旧的构建 3、配置Git信息 4、构建触发器 和 构建环境可以直接跳过 5、直接来到Build Step…

技术广度必备——高并发设计之分布式锁的实现方式

文章目录 问题背景前言实现基于MySQL实现唯一索引乐观锁悲观锁 基于Redis基于Zookeeper原理使用Curator框架实现ZK分布式锁缺点 问题背景 研究有哪几种方案可以实现分布式锁&#xff0c;技术选型的场景下能用到。 前言 本文参考过的文章有分布式锁的几种实现方式方式大致分为3种…

蓝帽杯 取证2022

网站取证 网站取证_1 下载附件 并解压 得到了一个文件以及一个压缩包 解压压缩包 用火绒查病毒 发现后门 打开文件路径之后 发现了一句话木马 解出flag 网站取证_2 让找数据库链接的明文密码 打开www文件找找 查看数据库配置文件/application/database.php&#xff08;CodeI…

Cuda和cuDNN安装

Cuda和cuDNN安装 1Cuda下载与安装1.1查看适合cuda的版本1.2下载cuda toolkit1.3cuda安装步骤1.4配置环境变量1.5验证 2cuDNN下载与安装2.1cuDNN下载2.2cuDNN配置2.3配置环境变量2.4验证 3安装PyTorch-GPU3.1打开Anaconda Prompt![在这里插入图片描述](https://img-blog.csdnimg…

ARM--day2(cpsr、spsr、数据搬移指令、移位操作指令、位运算操作指令、算数运算指令、比较指令、跳转指令)

.text .global _gcd _gcd:mov r0,#9mov r1,#15b loop loop:cmp r0,r1beq stopsubhi r0,r1bhi loopsubcc r1,r0bcc loopstop:b stop.end用for循环实现1~100之间和5050 .text .global _gcd _gcd:mov r0,#0x0mov r1,#0x1mov r2,#0x64b loop loop:cmp r1,r2bhi stopadd r0,r0,r1ad…

卷王特斯拉又全网降价了,卷死车企们

哈喽,大家好,今天媒介盒子小编又来跟大家分享软文推广的干货知识了,本篇分享的主要内容是&#xff1a;特斯无孔不入的营销手段。 1、特斯拉Model Y降价 车企要打架 自2023 年 8 月 14 日起&#xff0c;Model Y 长续航版起售价从 31.39 万元调整为 29.99 万元&#xff0c;Mode…

React+Typescript清理项目环境

上文 创建一个 ReactTypescript 项目 我们创建出了一个 React配合Ts开发的项目环境 那么 本文 我们先将环境清理感觉 方便后续开发 我们先来聊一下React的一个目录结构 跟我们之前开发的React项目还是有一些区别 public 主要是存放一些静态资源文件 例如 html 图片 icon之类的 …

走进 Linux

一、开关机 开机&#xff1a; 开机会启动许多程序。他们在windows叫做“服务”(service),在Linux就叫做“守护进程”&#xff08;daemon&#xff09;开机成功后&#xff0c;它会显示一个文本登录界面&#xff0c; 这个界面就是我们经常看到的登录界面&#xff0c;在这个登录界…

【有奖体验】COS插件体验,四重好礼等你拿!

对象存储 COS 为降低用户接入门槛&#xff0c;集成了多款 COS 插件&#xff0c;开放供用户使用&#xff0c;包含搭建网站、图床、论坛等多个热门业务场景的插件&#xff0c;让使用更便捷&#xff01;对象存储 COS 准备了多重好礼&#xff0c;欢迎广大同学们踊跃体验 COS 插件&a…

U盘安装CentOS7系统出现dracut timeout的解决办法

文章目录 业务场景操作步骤U盘装CentOS7系统确定U盘盘符修改启动命令系统配置 总结 业务场景 我们在某市实施交通信控平台项目&#xff0c;我们申请了一台服务器&#xff0c;用于平台安装由于机房机器只有内网&#xff0c;不连互联网&#xff0c;我们无法安装所需要的软件&…