线程池
线程池
曦暮流年简介
线程池本质上就是一组线程的集合,当使用线程时会从集合中取出一个线程,当时用完后,该线程会重新回到集合中
参数
Java中创建线程池:
1 | 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
(丢弃队列中最旧的任务并尝试重新提交当前任务)
核心线程不会再创建线程池的一瞬间全部创建完成,直接常见的线程池是空的。只有当任务使用线程持的时候才会创建一个线程,直到数量等于核心线程数(哪怕之前的线程已经完成任务也不会使用之前的线程而是继续创建)。
如果想要再线程池创建的时候预创建线程可以使用以下两个方法
1
2
3
4 // 启动一个核心线程(前提是线程池中的线程数量没有达到最大核心线程数)
pool.prestartCoreThread();
// 启动所有核心线程
pool.prestartAllCoreThreads();
核心线程数
对于CPU密集型任务:CPU核心数 + 1
IO密集型任务(文件IO、网络IO):
- 公式一:CPU核心数 * 2
- 公式二:CPU核心数 * (1 + 线程等待时间 / 线程运行时间)
- 线程等待时间:线程没有使用CPU的时间,如:阻塞IO
- 线程运行时间:线程执行完某个任务的总时间
1 | // 获取CPU内核数量 |
执行任务具体流程
1 | public void execute(Runnable command) { |
五种状态
1 | 原文: |
通过调用以下任意方法进入状态
1 | pool.shutdown(); // SHUTDOWN |
以上任意方法执行完成之后
线程池中的所有线程都会被关闭(队列不一定清空),然后进入 TIDYING 状态。当进入到 TIDYING 状态后会调用 terminate()
方法(这是一个钩子函数,可以被重写一些逻辑),然后进入 TERMINATED 状态
当执行两种方法时是先设置状态然后再依照对应的规则关闭线程,为的就是防止后续任务的提交