1.构造参数详细讲解
-
corePoolSize:常驻核心线程数,也就是说这是线程池中始终存活的线程,即使是空闲状态,也不会被销毁,除非关闭线程池。但有个特殊,设置了allowCoreThreadTimeout
-
maximumPoolSize:最大线程数,线程池中最多能存活的线程数
-
keepAliveTime:空闲线程存活时间,配合下一个参数unit,当线程池中的线程数量超过corePoolSize,会清空多余空闲线程,以便使线程池数量保持到corePoolSize数量
-
unit: 空闲线程存活时间的单位,如TimeUnit.SECONDS、TimeUnit.MILLISECONDS等
-
workQueue: 工作队列(阻塞队列、等待队列),调用submit方法,任务首先进入此队列中,而不是立即执行,队列的容量决定了能有多少请求被阻塞队列接受
-
threadFactory: 线程工厂,一般使用默认Executors.defaultThreadFactory()即可
-
handler:线程池的拒绝策略。当等待队列已经满了,再也塞不下新的任务;同时,线程池也满了(即达到maximumPoolSize及workQueue满),无法为新任务服务。这时,我们需要拒绝策略机制合理的处理这个问题
2. 任务什么时候被执行
有时候,您会发现提交的任务,并没有被立即执行,只执行了corePoolSize个任务。这个可能会给我们带来困惑,还没有达到maximumPoolSize,怎么还有部分任务不执行呢?其实是新任务进入了workQueue,workQueue没有满。有二种情况下,剩余部分任务会被执行,第一种workQueue满了,第二种线程池中有线程执行完任务后。
-
初次分配时,线程池会分配corePoolSize数量的线程,用于执行当前提交的任务
-
任务提交超过corePoolSize数量的部分,会暂时放至workQueue;待线程池中有空闲线程时,会从workQueue取任务
-
提交的任务过多,以至于workQueue也放下去了,线程池会在分配线程直到maximumPoolSize;如果线程池中的某个线程执行完了,又没有达到keepAliveTime时,会从workQueue取任务
-
提交的任务更多了,以至于超过了maximumPoolSize和workQueue大小,会执行handler策略
public class ThreadPoolExecutorBuild {public static void main(String[] args) {ThreadPoolExecutor executorService &#61; (ThreadPoolExecutor)buildThreadPoolExecutor();int activeCount &#61; -1;int queueSize &#61; -1;while(true){if (activeCount !&#61; executorService.getActiveCount() || queueSize !&#61; executorService.getQueue().size()) {System.out.println(executorService.getActiveCount());System.out.println(executorService.getCorePoolSize());System.out.println(executorService.getMaximumPoolSize());System.out.println(executorService.getQueue().size());activeCount &#61; executorService.getActiveCount();queueSize &#61; executorService.getQueue().size();System.out.println("&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;");}}}private static ExecutorService buildThreadPoolExecutor(){ExecutorService executorService &#61; new ThreadPoolExecutor(1, 2,30, TimeUnit.SECONDS,new ArrayBlockingQueue<>(1),r->{Thread t &#61; new Thread(r);return t;},new ThreadPoolExecutor.AbortPolicy());return executorService;}private static void sleepSeconds(long seconds){System.out.println("* " &#43; Thread.currentThread().getName() &#43; " *");try {TimeUnit.SECONDS.sleep(seconds);} catch (InterruptedException e) {e.printStackTrace();}}
}
(1)executorService.execute(()->sleepSeconds(10)); * Thread-0 *&#61;&#61;&#61;&#61;&#61;14:55:04&#61;&#61;&#61;&#61;&#61;1120&#61;&#61;&#61;&#61;&#61;14:55:14&#61;&#61;&#61;&#61;&#61;0120(2)executorService.execute(()->sleepSeconds(5));executorService.execute(()->sleepSeconds(10)); * Thread-0 *&#61;&#61;&#61;&#61;&#61;14:56:28&#61;&#61;&#61;&#61;&#61;1121* Thread-0 *&#61;&#61;&#61;&#61;&#61;14:56:33&#61;&#61;&#61;&#61;&#61;1120&#61;&#61;&#61;&#61;&#61;14:56:43&#61;&#61;&#61;&#61;&#61;0120(3)executorService.execute(()->sleepSeconds(10));executorService.execute(()->sleepSeconds(5));executorService.execute(()->sleepSeconds(8));* Thread-0 ** Thread-1 *&#61;&#61;&#61;&#61;&#61;15:00:22&#61;&#61;&#61;&#61;&#61;2121* Thread-1 *&#61;&#61;&#61;&#61;&#61;15:00:30&#61;&#61;&#61;&#61;&#61;2120&#61;&#61;&#61;&#61;&#61;15:00:32&#61;&#61;&#61;&#61;&#61;1120&#61;&#61;&#61;&#61;&#61;15:00:35&#61;&#61;&#61;&#61;&#61;0120
3.线程池的关闭
- submit()、execute()&#xff1a;非阻塞方法&#xff0c;会立即返回&#xff0c;因为任务首次会放置在workQueue&#xff0c;然后线程池在从workQueue取任务执行
- shutdown()&#xff1a;非阻塞方法&#xff0c;会立即返回
- shutdownNow()&#xff1a;非阻塞方法&#xff0c;会立即返回
- awaitTermination()&#xff1a;阻塞方法&#xff0c;接收timeout和TimeUnit两个参数&#xff0c;用于设定超时时间及单位。当等待超过设定时间时&#xff0c;会监测ExecutorService是否已经关闭&#xff0c;若关闭则返回true&#xff0c;否则返回false。一般情况下会和shutdown方法组合使用。
3.1 shutdown和shutdownNow区别
(1) shutdown
- 开始一个有序的关闭&#xff0c;在关闭中&#xff0c;之前提交的任务会被执行&#xff08;包含正在执行的&#xff0c;在阻塞队列中的&#xff09;
- 新任务会被拒绝
- 中断空闲线程
- 当前方法不会等待之前提交的任务执行结束&#xff0c;可以使用awaitTermination()
(2) shutdownNow
- 返回正在等待被执行的任务列表
- 新任务会被拒绝
- 中断所有线程&#xff08;shutdown只是中断空闲线程&#xff09;
(3) isShutdown
只要调用了这两个关闭方法中的任意一个&#xff0c;isShutdown方法就会返回true
(4) isTerminated
当所有的任务都已关闭后&#xff0c;才表示线程池关闭成功&#xff0c;这时调用isTerminaed方法会返回true
(5) 中断
如果线程中没有sleep 、wait、Condition、定时锁等可中断点&#xff0c;interrupt()方法是无法中断当前的线程的。所以&#xff0c;ShutdownNow()并不代表线程池就一定立即就能退出&#xff0c;它可能必须要等待所有正在执行的任务都执行完成了才能退出
4.监控线程池
(1) 如果在系统中大量使用线程池&#xff0c;则有必要对线程池进行监控&#xff0c;方便在出现问题时&#xff0c;可以根据线程池的使用状况快速定位问题。
(2)可以通过线程池提供的参数进行监控&#xff0c;在监控线程池的时候可以使用以下属性&#xff1a;
- taskCount&#xff1a;线程池需要执行的任务数量&#xff0c;是个近似值。
- completedTaskCount&#xff1a;线程池在运行过程中已完成的任务数量&#xff0c;小于或等于taskCount&#xff0c;是个近似值。
- largestPoolSize&#xff1a;线程池里曾经创建过的最大线程数量。通过这个数据可以知道线程池是否曾经满过。如该数值等于线程池的最大大小&#xff0c;则表示线程池曾经满过。
- poolSize&#xff1a;线程池当前的线程数总量&#xff0c;包括活动的线程与闲置的线程。
- activeCount&#xff1a;获取活动的线程数。