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

线程池的选择

  • 一:故事背景
  • 二:线程池原理
    • 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.rhkb.cn/news/89962.html

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

相关文章

POI处理excel,根据XLOOKUP发现部分公式格式不支持问题

poi4不支持XLOOKUP函数,但poi最新的5.2.3却已经对此函数做了支持 poi下载地址:Index of /dist/poi/release/bin 公式源码位置:org/apache/poi/ss/formula/atp/XLookupFunction.java 但是在使用此函数过程中,发现有些XLOOKUP函数会…

网络设备(防火墙、路由器、交换机)日志分析监控

外围网络设备(如防火墙、路由器、交换机等)是关键组件,因为它们控制进出公司网络的流量。因此,监视这些设备的活动有助于 IT 管理员解决操作问题,并保护网络免受攻击者的攻击。通过收集和分析这些设备的日志来监控这些…

Oracle database Linux自建环境备份至远端服务器自定义保留天数

环境准备 linux下安装oracle 请看 oracle12c单节点部署 系统版本: CentOS 7 软件版本: Oracle12c 备份策略与实现方法 此次备份依赖Oracle自带命令exp与linux下crontab命令(定时任务) exp Oracle中exp命令是一个用于导出数据库数据和对象的…

虚拟机内搭建CTFd平台搭建及CTF题库部署,局域网内机器可以访问

一、虚拟机环境搭建 1、安装docker、git、docker-compose ubuntu: sudo apt-get update #更新系统 sudo apt-get -y install docker.io #安装docker sudo apt-get -y install git #安装git sudo apt-get -y install python3-pip #安装pip3 sudo pip install dock…

JavaEE初阶:多线程 - 编程

1.认识线程 我们在之前认识了什么是多进程,今天我们来了解线程。 一个线程就是一个 "执行流". 每个线程之间都可以按照顺讯执行自己的代码. 多个线程之间 "同时" 执行 着多份代码. 引入进程这个概念,主要是为了解决并发编程这样的…

时序预测 | MATLAB实现CNN-BiGRU-Attention时间序列预测

时序预测 | MATLAB实现CNN-BiGRU-Attention时间序列预测 目录 时序预测 | MATLAB实现CNN-BiGRU-Attention时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 MATLAB实现CNN-BiGRU-Attention时间序列预测,CNN-BiGRU-Attention结合注意力机制时…

机器学习笔记之优化算法(十二)梯度下降法:凸函数VS强凸函数

机器学习笔记之优化算法——梯度下降法:凸函数VS强凸函数 引言凸函数:凸函数的定义与判定条件凸函数的一阶条件凸函数的梯度单调性凸函数的二阶条件 强凸函数强凸函数的定义强凸函数的判定条件强凸函数的一阶条件强凸函数的梯度单调性强突函数的二阶条件…

【从零学习python 】21.Python中的元组与字典

文章目录 元组一、访问元组二、修改元组三、count, index四、定义只有一个数据的元组五、交换两个变量的值 字典介绍一、列表的缺点二、字典的使用进阶案例 元组 Python的元组与列表类似,不同之处在于元组的元素不能修改。元组使用小括号,列表使用方括号…

C++初阶之一篇文章教会你queue和priority_queue(理解使用和模拟实现)

queue和priority_queue(理解使用和模拟实现) 什么是queuequeue的使用1.queue构造函数2.empty()3.size()4.front()5.back();6.push7.emplace8.pop()9.swap queue模拟实现什么是priority_queuepriority_queue的使用1.priority_queue构造函数1.1 模板参数 C…

论文阅读 RRNet: A Hybrid Detector for Object Detection in Drone-captured Images

文章目录 RRNet: A Hybrid Detector for Object Detection in Drone-captured ImagesAbstract1. Introduction2. Related work3. AdaResampling4. Re-Regression Net4.1. Coarse detector4.2. Re-Regression 5. Experiments5.1. Data augmentation5.2. Network details5.3. Tra…

DP(区间DP)

目录 石子合并 合并果子(贪心 Huffman树) 环形石子合并 石子合并 设有 N 堆石子排成一排,其编号为 1,2,3,…,N。 每堆石子有一定的质量,可以用一个整数来描述,现在要将这 N 堆石子合并成为一堆。 每次只能合并相邻…

全文检索与日志管理 Elasticsearch(上)

一、Elasticsearch介绍 1.1 全文检索索引 Elasticsearch是一个全文检索服务器,全文检索是一种非结构化数据的搜索方式。 那么什么是结构化数据和非结构化数据呢? 结构化数据:指具有固定格式固定长度的数据,如数据库中的字段。 …

如何有效开展网络安全事件调查工作

网络安全事件调查是现代企业网络安全体系建设的关键组成部分。为了防止网络攻击,仅仅关注于安全工具的应用效果远远不够,因为安全事件一直都在发生。安全团队只有充分了解攻击者的行踪和攻击路径,才能更好地防范更多攻击时间的发生。 做好网…

基于Python爬虫+词云图+情感分析对某东上完美日记的用户评论分析

🤵‍♂️ 个人主页:艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞&#x1f4…

【go语言学习笔记】05 Go 语言实战

文章目录 一、 RESTful API 服务1. RESTful API 定义1.1 HTTP Method1.2 RESTful API 规范 2. RESTful API 风格示例3. RESTful JSON API4. Gin 框架4.1 导入 Gin 框架4.2 使用 Gin 框架4.2.1 获取特定的用户(GET)4.2.2 新增一个用户(POST&am…

ElasticSearch安装与介绍

Elastic Stack简介 如果没有听说过Elastic Stack,那你一定听说过ELK,实际上ELK是三款软件的简称,分别是Elasticsearch、 Logstash、Kibana组成,在发展的过程中,又有新成员Beats的加入,所以就形成了Elastic…

9月大理,Move HackerHouse,成为全球数字游民的第一站

🚀世界各地的 hacker 们!即日起,我们正式向您发出 co-buiding & co-living 的邀请! 9.3日至9.24日,为期3周的 Move 主题Antalpha HackerHouse 将坐落于大理,邀请所有 Web3 开发者一起探索 Move 生态发…

基于Selenium模块实现无界面模式 执行JS脚本

此篇文章主要介绍如何使用 Selenium 模块实现 无界面模式 & 执行JS脚本(把滚动条拉到底部),并以具体的示例进行展示。 1、Selenium 设置无界面模式 创建浏览器对象之前,创建 options 功能对象 :options webdriver.ChromeOptions() 添加…

微服务系列(2)--注册中心

在博文:微服务系列(1)里我们提到过注册中心的概念,简单来说微服务注册中心是一个用于存储和管理微服务实例信息的组件,它提供了服务注册、服务发现、服务健康检查等功能,以确保微服务之间的稳定通信。在微服务架构中,各…

Python 图形界面框架TkInter(第八篇:理解pack布局)

前言 tkinter图形用户界面框架提供了3种布局方式,分别是 1、pack 2、grid 3、place 介绍下pack布局方式,这是我们最常用的布局方式,理解了pack布局,绝大多数需求都能满足。 第一次使用pack() import …