本文针对初学者可能有点不适合,当对多线程有点了解,再看这些属性的说明会理解该线程池工厂的一些特性;
类图:
======================================================================
有情提醒:
线程池不使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样 的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明: Executors 返回的线程池对象的弊端如下:
FixedThreadPool 和 SingleThreadPool : 允许的请求队列长度为 Integer.MAX_VALUE ,可能会堆积大量的请求,从而导致 OOM 。
CachedThreadPool 和 ScheduledThreadPool : 允许的创建线程数量为 Integer.MAX_VALUE ,可能会创建大量的线程,从而导致 OOM 。
1. 线程池中主要字段说明
线程池控制状态,ctl,是一个原子封装的integer.它包含了两个概念字段:
workerCount: 表示有效的线程数;
runState:表示是否是运行,停止等等状态;
1.1 workerCount
为了将它们集成到一个int中,我们限制workerCount值是(2^29)-1 大约500million(5亿)个线程,
如果在以后这个值的大小有问题,可以给修改成AtomicLong,并且需要调整偏移量,掩码常量;
但是直到真正的需要出现前,使用int将让代码更快更简单;
workerCount代表线程已经启动工作的数量,并不代表停止的数量;该值可能与实际存活的线程数量不同;
例如:当TreadFactory创建一个请求线程失败时,
例如,当ThreadFactory无法创建线程时
询问,并且退出线程仍在执行时
终止之前记账。 用户可见池大小报告为工作集的当前大小。
1.2 runState
runState 提供了整个生命周期的控制,主要有以下几种值:
RUNNING: 接受新的任务,并且处理队列中已经有的任务;
SHUTDOWN:不会再接受新的任务,但是还会处理队列中的任务;
STOP:不能够接受新的任务,不再处理队列中的任务,并且interrupt中断现在执行的任务;
TIDYING: 所有的任务都终止了,workerCount是0,线程一直处在TIDYING状态直到调用terminated()方法;
TERMINATED: terminated()方法执行完成了
这些值之间的数值顺序变化很重要,可以允许有序的变化。 runState值是单调增加,不会变小,但不必命中每个状态。 值的变化有:
RUNNING -> SHUTDOWN
调用shutdown()方法,也有可能是隐匿的执行finalize()方法时(因为该方法内部也是调用shutdown()方法)。
(RUNNING or SHUTDOWN) -> STOP
调用showdownNow()方法
SHUTDOWN -> TIDYING
当线程池和队列都是空时 可以通过队列的isEmpty() 方法来判断线程池是否为空
STOP -> TIDYING
线程池是空的
TIDYING -> TERMINATED
调用terminated()完成后
1.3 workQueue
这个队列是持有任务并且能够拿出来给到工作者线程;当workQueue.isEmpty() 时我们不要求workQueue.poll() 方法立即返回null值回来。所以我们可以通过workQueue.isEmpty()查看队列是否为空。(例如我们想将线路池的状态从SHUTDOWN 转换到TIDYING时,可以通过该种方式来判断)
但是会有特殊的队列存在,如果我们使用的是DelayQueeues时,在调用poll()方法时如果超时了允许返回null
1.4 mainLock
保持访问工作线程集合和相关的工作线程的锁。虽然我们可以使用某种并发集,但事实证明通常使用锁还是最好的。 原因之一是
这会序列化interruptIdleWorkers,从而避免了不必要的中断风暴,尤其是在关机期间。否则退出线程会同时中断那些尚未中断的线程。 这也简化了一些最大的PoolSize等的相关统计簿记。我们还要在shutdown和shutdownNow时保持mainLock,以便确保工作线程集合稳定下来时能够检查哪些允许中断和哪些实际中断。
1.5 workers
包含了所有在池中的工作线程。只有持有mainLock锁才能够访问该集合;
1.6 volatile修饰的属性
以下这些值保证了可见性。因些这些参数不需要mainLock锁来持有访问,当我们访问时可以拿到最新的值;
threadFactory: 新线程的工厂。所有线程都使用此工厂创建(通过调用该工厂的addWorker方法)。所有调用者必须做好使用addWorker添加失败的准备,这可以通过系统或用户提供的限制线程数后使用的策略来处理。即使不是线程本身的error,创建线程失败时可能会导致新任务被拒绝或现有任务被卡在 队列中。
我们运行的越久,在尝试创建线程时,可能会抛出诸如OutOfMemoryError的错误。这个错误是因为,在Thread.start方法执行时需要分配本机堆栈,并且用户需要清理已经很干净的池关闭。那里可能有足够的内存供清理代码使用完成而不会遇到另一个OutOfMemoryError。
handler: 当线程饱合或者执行关闭程序时才会调用该handler。
系统中提供的饱合策略有:
默认的饱合策略是AbortPolicy。如果线程池的队列饱合后,会抛出throw new RejectedExecutionException异常;
DiscardPolicy:丢失策略。当饱合时该条任务直接丢弃掉;
DiscardOldestPolicy:丢弃掉老的。会丢弃掉未处理的请求任务中最老的任务。通过获取当前的workQueue的poll()方法丢失掉,然后执行继续执行ThreadPoolExecutor的execute方法执行该任务,如果队列还是满的,还是执行该策略,直接访任务能够添加到队列,或者能被直接执行为止;但是如果是执行者被关闭了,那么该条任务也会被丢弃掉;
CallerRunsPolicy:如果执行者没有shutdown,那么这个任务会被直接调用run方法来执行。如果ThreadPoolExecutor已经shutDown了,那么这个任务也会被丢弃掉;
通过以上说明也可以看出,饱合策略是遵循了线程池的runState状态字段的。而上面的shutDown并不是指线程池的runState是在shutdown这个值,而是只要不是running即可,因为调用的是isShutDown()方法
keepAliveTime: 空闲的线程等待超时的时间(以纳秒单位)。当线程数据超过corePoolSize时,或者设置allowCoreThreadTimeOut为true时,空闲的线程的超时时间会生效;否则线程会一直等待从事新的工作,不会停止掉;
allowCoreThreadTimeOut:默认值是false,空闲的核心线程会一直存活动。 如果设置为true时,空闲的核心线程会保持到keepAliveTime这个时长;
corePoolSize:核心线程数是保持存活的workes的最小数量(如果没有设置核心线程数超时的话);如果设置了allowCoreThreadTimeOut为true,那么最小的数量就是0
maximumPoolSize:最大线程数量。注意实际最大值是受线程池容量的限制的;
1.6 其它一些属性说明
largestPoolSize : 跟踪达到最大池的大小。只有在持有mainLock锁的情况下才能够访问
completedTaskCount: 完成任务的计数器,仅仅在工作线程终止后才会进行更新;该参数也是只有在持有mainLock锁的情况下才能够访问;
2. 内部重要类说明
2.1 Worker
/ **
* Class Worker主要维护中断控制状态
*线程运行任务,以及其他次要簿记。
*此类是机会地扩展AbstractQueuedSynchronizer
*简化获取和释放每个锁的锁定
*任务执行。 这样可以防止出现以下情况的中断:
*旨在唤醒等待任务执行的工作线程
*而是中断正在运行的任务。 我们实现一个简单的
*非重入互斥锁,而不是使用
* ReentrantLock,因为我们不希望工作人员能够执行任务
*当他们调用诸如池控制方法时,重新获取锁
* setCorePoolSize。 此外,抑制中断直到
*线程实际上开始运行任务,我们初始化锁
*声明为负值,并在启动时将其清除(在
* runWorker)。
* /
3. 方法说明
3.1 execute(Runnable)
在将来的某个时间会执行给定的任务。这个任务有可能在新线程中执行,也有可能是在池中已经存在的线程中进行执行;
如果这个任务不能submitted执行,原因就是这个执行器已经shutdown,或者是因为容量已经达到了。那么些时这个任务就是饱合策略来执行了;
处理分为三个步骤:
第一步:如果当前运行的线程数据小于核心线程数量时,就会尝试启动一个新的线程来执行这个任务,并且worke的first task就是当前的任务;
第二步:如果当前线程池还在行动中,就将要执行的任务放入到队列中;然后二次检查一下当前线程池的状态,如果线程池的状态不是在运行中,就将当前的任务从队列中移出;并且执行饱合策略;如果在运行状态,但是当前没有线程运行了,那么就需要新添加一个线程;
第三步:如果当前线程不在运行中,或者不能添加到队列中,那么就尝试创建一个新的线程来执行,因为有可能线程的数量还没有超过最大线程数量;如果还是添加不成功,那么也会执行饱合策略;
相关推荐
Java ThreadPoolExecutor参数深入理解 Java ThreadPoolExecutor是Java并发编程中一个非常重要的组件,它提供了一种灵活的方式来管理线程池。ThreadPoolExecutor的参数深入理解是Java开发人员需要掌握的重要知识点,...
ThreadPoolExecutor 的核心参数包括 corePoolSize、maxPoolSize 和 keep-alive time。 * corePoolSize: 表示线程池的核心线程数。这是线程池的基本容量,表示线程池中总是存在的线程数。 * maxPoolSize: 表示线程池...
ThreadPoolExecutor使用和思考
ThreadPoolExecutor源码解析.md
了解ThreadPoolExecutor的这些核心概念和工作流程,能帮助开发者更好地调整线程池参数,优化并发性能,以及在系统出现问题时进行排查和定位。在实际开发中,合理配置线程池参数,避免线程池过载,是保障系统稳定运行...
创建线程池使用`ThreadPoolExecutor`构造函数,其参数含义如下: - `corePoolSize`: 核心线程数,表示线程池中保持的最小线程数。 - `maximumPoolSize`: 最大线程数,定义了线程池允许的最大并发线程数。 - `...
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long
线程池原理-ThreadPoolExecutor源码解析 1.构造方法及参数 2.阻塞对列: BlockingQueue 3.线程工厂: DefaultThreadFactory 4.拒绝策略: RejectedExecutionHandler 5.执行线程 Executor
(转)线程池:java_util_ThreadPoolExecutor 比较详细的介绍了ThreadPoolExecutor用法与属性
ThreadPoolExecutor的使用和Android常见的4种线程池使用介绍
除了通过构造函数自定义线程池外,Java还提供了一些预定义的线程池,它们通过 `Executors` 工具类创建,适用于不同的应用场景。 1. **newFixedThreadPool**:创建一个固定大小的线程池,线程池的大小由参数 `...
线程池ThreadPoolExecutor实战及其原理分析(下)线程池ThreadPoolExecutor实战及其原理分析(下)线程池ThreadPoolExecutor实战及其原理分析(下)线程池ThreadPoolExecutor实战及其原理分析(下)线程池ThreadPoolExecutor...
NULL 博文链接:https://bijian1013.iteye.com/blog/2284676
- **线程池配置**:根据系统资源和需求,合理配置ThreadPoolExecutor的参数,如核心线程数、最大线程数、队列容量和超时策略等。 5. **优点**: - **解耦**:将任务存储和执行分开,使得系统更灵活,易于扩展。 ...
ThreadPoolExecutor有几个构造函数,最多参数的构造函数最常用,下面会详细介绍各个参数的含义及其几个参数之间的关系: <span xss=removed>ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, ...
"JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用" JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用是Java多线程编程中的一种重要概念。随着多线程编程的普及,线程池的使用变得...
死磕ThreadPoolExecutor线程池.pdf!!死磕ThreadPoolExecutor线程池.pdf死磕ThreadPoolExecutor线程池.pdf死磕ThreadPoolExecutor线程池.pdf
今天,我们将对线程池ThreadPoolExecutor进行详细的介绍,并提供一些实际的使用示例。 一、线程池ThreadPoolExecutor简介 ThreadPoolExecutor是Java中的一个类,它实现了Executor接口,用于管理线程池中的线程。...