线程池
简介
线程池本质上就是一组线程的集合,当使用线程时会从集合中取出一个线程,当时用完后,该线程会重新回到集合中

参数
Java中创建线程池:
new ThreadPoolExecutor(10, 200, 1000, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(200))
corePoolSize(核心线程数):
这是线程池中会始终保持活动的线程数,即使它们目前是空闲的。这些线程不会因为执行完任务后处于空闲状态而被终止,除非设置了
allowCoreThreadTimeOut
为true
。maximumPoolSize(最大线程数):
这是线程池能够容纳同时执行的最大线程数。当活动线程数达到这个值后,新来的任务将会被阻塞或拒绝,具体行为取决于所使用的阻塞队列和拒绝策略。
keepAliveTime(空闲线程存活时间):
如果线程池中的线程数量超过
corePoolSize
,那么多余的空闲线程在等待新任务到达的最长时间。超过这个时间后,多余的空闲线程会被终止。这个参数对核心线程不生效,除非设置了allowCoreThreadTimeOut
为true
。unit(空闲线程存活时间单位):
用来指定
keepAliveTime
参数的时间单位,如TimeUnit.MILLISECONDS
(毫秒)、TimeUnit.SECONDS
(秒)等。workQueue(工作队列/任务队列):
当新任务到来时,如果线程池中的线程数量已经达到了
maximumPoolSize
,那么新任务会被放入这个队列中等待执行。根据不同的队列类型(如LinkedBlockingQueue
、ArrayBlockingQueue
、SynchronousQueue
等),线程池的行为会有很大不同。
除了以上的五重必备的参数,还有两种可选的参数
threadFactory(线程工厂):
用于创建新线程的工厂,可以用来设置新线程的优先级、名称、是否为守护线程等属性。
rejectedExecutionHandler(拒绝策略):
当线程池和工作队列都满时,对于新提交的任务所采取的策略,比如
AbortPolicy
(抛出异常)、CallerRunsPolicy
(调用者运行)、DiscardPolicy
(静默丢弃)或DiscardOldestPolicy
(丢弃队列中最旧的任务并尝试重新提交当前任务)
核心线程不会再创建线程池的一瞬间全部创建完成,直接常见的线程池是空的。只有当任务使用线程持的时候才会创建一个线程,直到数量等于核心线程数(哪怕之前的线程已经完成任务也不会使用之前的线程而是继续创建)。
如果想要再线程池创建的时候预创建线程可以使用以下两个方法
// 启动一个核心线程(前提是线程池中的线程数量没有达到最大核心线程数) pool.prestartCoreThread(); // 启动所有核心线程 pool.prestartAllCoreThreads();
核心线程数
对于CPU密集型任务:CPU核心数 + 1
IO密集型任务(文件IO、网络IO):
- 公式一:CPU核心数 * 2
- 公式二:CPU核心数 * (1 + 线程等待时间 / 线程运行时间)
- 线程等待时间:线程没有使用CPU的时间,如:阻塞IO
- 线程运行时间:线程执行完某个任务的总时间
// 获取CPU内核数量
int numberOfCores = Runtime.getRuntime().availableProcessors();
执行任务具体流程
public void execute(Runnable command) {
// 如果传过来的任务是空的,则报空指针异常
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 如果还没有达到最大核心线程数则创建一个新的线程
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 如果达到最大核心数,则把任务入队(最大数取决于构造函数设置)等核心线程数有空闲再执行
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 如果队列满了,则创建新的临时线程(最大数取决于 最大线程数-核心线程数)
// 临时线程再处理完自己的任务之后也会去帮忙处理队列里面的任务
// 这种线程如果空闲时间超过一定限制则会被销毁
else if (!addWorker(command, false))
// 如果走到这一步就代表已经达到最大资源,走默认的拒绝策略
reject(command);
}

五种状态
原文:
RUNNING: Accept new tasks and process queued tasks
SHUTDOWN: Don't accept new tasks, but process queued tasks
STOP: Don't accept new tasks, don't process queued tasks,
and interrupt in-progress tasks
TIDYING: All tasks have terminated, workerCount is zero,
the thread transitioning to state TIDYING
will run the terminated() hook method
TERMINATED: terminated() has completed
翻译:
RUNNING:接受新任务并处理排队任务
SHUTDOWN:不接受新任务,但处理排队任务
STOP:不接受新任务,不处理排队任务,中断正在进行的任务
TIDYING:所有任务都已终止,workerCount为零,线程转换到 TIDYING 状态,会运行terminate()钩子方法
TERMINATED:terminated()已完成
通过调用以下任意方法进入状态
pool.shutdown(); // SHUTDOWN
pool.shutdownNow(); // STOP
以上任意方法执行完成之后
线程池中的所有线程都会被关闭(队列不一定清空),然后进入 TIDYING 状态。当进入到 TIDYING 状态后会调用 terminate()
方法(这是一个钩子函数,可以被重写一些逻辑),然后进入 TERMINATED 状态
当执行两种方法时是先设置状态然后再依照对应的规则关闭线程,为的就是防止后续任务的提交