首页 > 其他分享 >线程池 --- 基础总结

线程池 --- 基础总结

时间:2024-07-19 23:54:17浏览次数:22  
标签:总结 状态 队列 --- 任务 线程 执行 CPU

使用线程池主要为了解决一下几个问题:

通过重用线程池中的线程,来减少每个线程创建和销毁的性能开销。

对线程进行一些维护和管理,比如定时开始,周期执行,并发数控制等等。

一、Executor接口关系

Executor是一个接口,跟线程池有关的基本都要跟他打交道。下面是常用的ThreadPoolExecutor的关系。

  • Executor:接口很简单,只有一个execute方法。
  • ExecutorService:是Executor的子接口,增加了一些常用的对线程的控制方法,之后使用线程池主要也是使用这些方法。
  • AbstractExecutorService:是一个抽象类。ThreadPoolExecutor就是实现了这个类。
  • ThreadPoolExecutor:是创建线程池的核心类,对核心方法进行了实现。
  • Executors :没有任何继承关系,对ThreadPoolExecutor的再封装,定义了newFixedThreadPool(),newSingleThreadExecutor()等

二、多种线程池创建方式:

  1. Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待;
  2. Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程;
  3. Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序;
  4. Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池;
  5. Executors.newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池;
  6. Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定)【JDK 1.8 添加】。
  7. ThreadPoolExecutor:最原始的创建线程池的方式。

三、ThreadPoolExecutor机制

1. 线程池状态

线程池的状态要和线程的状态区分开来,初学者可能会记混淆。

线程池的状态运行状态共有5种,分别是:

  • RUNNING:运行中, 为初始状态,即刚创建的线程池就是此状态。 
  • TERMINATED,终止状态,钩子函数terminated()已经执行完成,线程池彻底销毁。 
  • TIDYING:清理中,所有任务都停止了,且线程数量也降为0。
  • STOP:停止状态,不再接收新任务,不再处理已有任务,且会中断正在执行的任务。
  • SHUTDOWN:停工状态,不再接收新任务,但会继续处理队列中的任务。 

线程的状态对比着了解:

  1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
  2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
  3. 阻塞(BLOCKED):表示线程阻塞于锁。(线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态,进入同步队列
  4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。(通过调用线程的wait()方法,让线程等待某工作的完成,进入等待队列)
  5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。(通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。 )
  6. 终止(TERMINATED):表示该线程已经执行完毕。

2. 核心构造方法讲解

2.1构造方法参数讲解

参数名

作用

corePoolSize

核心线程数大小

maximumPoolSize

最大线程数大小

keepAliveTime

线程池中超过corePoolSize数目的空闲线程最大存活时间;可以allowCoreThreadTimeOut(true)使得核心线程有效时间

TimeUnit

keepAliveTime时间单位

workQueue

阻塞任务队列

threadFactory

新建线程工厂

RejectedExecutionHandler

当提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理

2.2 任务队列:

        线程池中的任务队列.常用的有三种队列,SynchronousQueue ,LinkedBlockingDeque ,ArrayBlockingQueue。

2.3 threadFactory:

线程工厂,提供创建新线程的功能。ThreadFactory是一个接口,只有一个方法

public interface ThreadFactory {
  Thread newThread(Runnable r);
}

通过线程工厂可以对线程的一些属性进行定制。

2.4拒绝策略:

RejectedExecutionHandler:RejectedExecutionHandler也是一个接口,只有一个方法

public interface RejectedExecutionHandler {
  void rejectedExecution(Runnable var1, ThreadPoolExecutor var2);
}

当线程池中的资源已经全部使用,添加新线程被拒绝时,会调用RejectedExecutionHandler的rejectedExecution方法。

jdk默认提供了四种拒绝策略:

  1. CallerRunsPolicy - 调用线程执行,只要线程池没有关闭的话,则使用调用线程直接运行任务。一般并发比较小,性能要求不高,不允许失败。但是,由于调用者自己运行任务,如果任务提交速度过快,可能导致程序阻塞,性能效率上必然的损失较大
  2. AbortPolicy - 终止执行,并抛出拒绝执行 RejectedExecutionException 异常信息。线程池默认的拒绝策略。必须处理好抛出的异常,否则会打断当前的执行流程,影响后续的任务执行。
  3. DiscardPolicy - 直接丢弃,其他啥都没有
  4. DiscardOldestPolicy - 丢弃老的任务,只要线程池没有关闭的话,丢弃阻塞队列 workQueue 中最老的一个任务,并将新任务加入

3.主要流程:

其中比较容易让人误解的是:corePoolSize,maximumPoolSize,workQueue之间关系。

1.当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。

2.当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行

3.当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务

4.当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理

5.当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程

6.当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭

【当提交任务数大于 corePoolSize 的时候,会优先将任务放到 workQueue 阻塞队列中。当阻塞队列饱和后,会扩充线程池中线程数,直到达到 maximumPoolSize 最大线程数配置。此时,再多余的任务,则会触发线程池的拒绝策略了。】

4.参数配置考虑

  1. CPU密集型任务,线程池的大小可以设置为CPU核心数加1(N+1N+1)或CPU核心数(NN)。这种设置考虑到了任务对CPU资源的消耗,以及一个额外的线程用于防止线程偶发的缺页中断或其他原因导致的任务暂停。当线程数量太小,大量请求可能被阻塞在线程队列中等待执行,导致CPU资源未得到充分利用;而线程数量太大,过多的上下文切换会增加执行时间,影响整体执行效率。
  2. IO密集型任务,线程池的大小可以设置为2倍的CPU核心数加1(2N+12N+1)或2倍的CPU核心数(2N2N)。这种设置适应于IO密集型任务中,系统大部分时间用于处理IO交互,而线程在处理IO的时间内不会占用CPU。通过增加线程数,可以更好地利用CPU资源,同时保持IO设备的忙碌状态。
  3. 实际配置时,除了考虑任务类型和服务器配置外,还需要考虑业务场景的具体需求,如并发量、一次业务的整体耗时等。通过测试和调整,找到一个合适的线程池大小,以达到最佳的性能和资源利用率。
  4. 环境因素也是影响线程池大小设置的重要因素。例如,如果主机上已经运行了其他线程或进程,如Tomcat容器、数据库连接池等,这些都会占用CPU资源,因此在设置线程池大小时需要考虑到这些因素。

注意点:

1、用ThreadPoolExecutor自定义线程池,看线程是的用途,如果任务量不大,可以用无界队列,如果任务量非常大,要用有界队列,防止OOM

2、如果任务量很大,还要求每个任务都处理成功,要对提交的任务进行阻塞提交,重写拒绝机制,改为阻塞提交。保证不抛弃一个任务

3、最大线程数一般设为2N+1最好,N是CPU核数

4、核心线程数,看应用,如果是任务,一天跑一次,设置为0,合适,因为跑完就停掉了,如果是常用线程池,看任务量,是保留一个核心还是几个核心线程数 。

5、如果要获取任务执行结果,用CompletionService,但是注意,获取任务的结果的要重新开一个线程获取,如果在主线程获取,就要等任务都提交后才获取,就会阻塞大量任务结果,队列过大OOM,所以最好异步开个线程获取结果

标签:总结,状态,队列,---,任务,线程,执行,CPU
From: https://blog.csdn.net/weixin_44146398/article/details/140534698

相关文章

  • Error: Assertion failed (nimages > 0) in cv::calibrateCameraRO, file D:\opencv4
    报错信息:Error:Assertionfailed(nimages>0)incv::calibrateCameraRO,fileD:\opencv4\opencv\opencv-4.1.0\modules\calib3d\src\calibration.cpp,line3691  原因:①图片路径问题,没有指向包含棋盘格的图片②图片中不包含棋盘格或者图片模糊等问题,导致查找棋盘......
  • 01-最大N个数和最小N个数的和
    题目给定一个数组,编写一个函数来计算它的最大N个数与最小N个数的和。你需要对数组进行去重。说明:*数组中数字范围[0,1000]*最大N个数与最小N个数不能有重叠,如有重叠,输入非法返回-1*输入非法返回-1思路很明显这是一个数组排序题,并且需要去重,因此很容易想到set集合,能很容......
  • 2024/07/19(暑假学习hadoop第二周总结)
    本周的学习任务主要是完成Hadoop中有关的组件的配置。有关于此配置的过程严格按照黑马程序员大数据入门到实战教程,大数据开发必会的Hadoop、Hive,云平台实战项目全套一网打尽_哔哩哔哩_bilibili来进行配置。首先就是HDFS的配置,这是Hadoop分布式文件系统,用于在多个服务器上构建存储......
  • 全网最详细保姆式讲解-汽车电子测试和认证标准科普解析(二)
    这些是汽车电子设备的常见电磁兼容性(EMC)和环境测试标准。以下是每个标准的简要说明:RE,CE(CISPR25,GB18655)RE(RadiatedEmission,辐射发射):测量设备辐射的电磁能量,防止其干扰其他电子设备。CE(ConductedEmission,传导发射):测量设备通过导线传导的电磁能量,防止其干扰......
  • 【简单介绍下DALL-E2,什么是DALL-E2?】
    ......
  • LeetCode题练习与总结:比较版本号--165
    一、题目描述给你两个 版本号字符串 version1 和 version2 ,请你比较它们。版本号由被点 '.' 分开的修订号组成。修订号的值 是它 转换为整数 并忽略前导零。比较版本号时,请按 从左到右的顺序 依次比较它们的修订号。如果其中一个版本字符串的修订号较少,则将缺失......
  • Mac终端美化(iterm2+oh-my-zsh+vim)
    vim+oh-my-zsh+git配置开发环境vim配置安装vundle使用vundle作为插件管理器,使用前先安装vundlemkdir-p~/.vim/bundlegitclonehttps://github.com/gmarik/Vundle.vim.git~/.vim/bundle/Vundle.vim 配置.vimrc编辑~/.vimrc文件,写入以下内容setnocompatible......
  • Keil烧录时出现Error: Flash Download failed - “Cortex-M0+“的解决办法
    在对MSPM0L1306mini板使用dapLink烧录例程时,程序能正常编译,但烧录时出现Error:FlashDownloadfailed - "Cortex-M0+"解决办法(同一个方法两种操作)操作1:操作2:两种操作最后打开的页面相同,最后几步操作也相同:点击【OK】保存修改烧录成功......
  • FreeRTOS【面试】实时操作系统的知识总结
    RTOS的实时性是如何实现的?任务之间是如何通信的?二值信号量与互斥信号量的区别?优先级反转?如何解决优先级反转问题?任务通知是怎么实现的?框架性的回答一个嵌入式系统Freertos的启动到结束的过程?任务切换的原理? 除了任务切换对freertos其他底层了解吗?讲讲FreeRT......
  • Python进阶(4)--正则表达式
    正则表达式在Python中,正则表达式(RegularExpression,简称Regex)是一种强大的文本处理工具,它允许你使用一种特殊的语法来匹配、查找、替换字符串中的文本。在这之前,还记得之前我们是通过什么方法分割字符串的嘛?strs="a,b;c@d"print(strs.split(",")) #以“,”为分割点分割......