上一篇《@Async注解的注意事项》说到@Async注解要配合自定义线程池一起使用,这一节说下Java的线程池。
public static void main(String[] args) {
// 定长线程池,核心线程数和最大线程数一致
ExecutorService executorService = Executors.newFixedThreadPool(10);
// 缓存线程池,核心线程数为0,最大线程数Integer最大值,线程存活时间超长
ExecutorService executorService1 = Executors.newCachedThreadPool();
// 定时线程池,可以设置执行时间
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
// 单线程线程池,核心线程和最大线程数都为1
ExecutorService executorService2 = Executors.newSingleThreadExecutor();
// 核心线程数为1的定时线程池,可以执行定时任务
ScheduledExecutorService scheduledExecutorService1 = Executors.newSingleThreadScheduledExecutor();
// 窃取线程池,并行执行线程,依赖cpu个数
ExecutorService executorService3 = Executors.newWorkStealingPool(3);
}
通过调试依次看下各个线程池的实际参数
newFixedThreadPool,定长线程池
可见,corePoolSize和maxinumPoolSize数是一样的,说明线程不会销毁,且不会动态扩容。灵活性较差
newCachedThreadPool,缓存线程池
corePoolSize为0,maxinumPoolSize为max integer,重点是keepAliveTime为600亿毫秒,线程一旦创建几乎不会销毁,容易造成OOM。
newScheduledThreadPool,定时线程池
maxinumPoolSize为max integer,可能造成OOM。主要特点是创建的ScheduledThreadPoolExecutor实现了ScheduledExecutorService接口,其中的schedule等方法可以设置定时执行任务。
newSingleThreadExecutor,单线程线程池
corePoolSize和maxinumPoolSize都为1,说明单个线程执行任务,未执行的任务只能堆叠在workQueue。
newSingleThreadScheduledExecutor,单线程定时线程池
事实上就是newScheduledThreadPool,只是corePoolSize设置为1
newWorkStealingPool,窃取线程池
这个线程池比较特殊,它是java8才提出来的,不是通过ThreadPoolExecutor创建,没有corePoolSize等参数。且线程是并行进行的,即依赖处理器进行。
我觉得下面这篇文章对我有帮助,可以阅读一下
以上就是通过Executors创建的6种线程池
通过Executors创建的6种线程池,除了java8提出的newWorkStealingPool,其他都是直接或者间接(子类)创建
ThreadPoolExecutor类实现的。
ThreadPoolExecutor主要有7大参数
corePoolSize,核心线程数,线程池默认线程数,线程池创建完成时,会初始化的线程数量,当有任务进来时,通过调度线程执行任务。
maxinumPoolSize,最大线程数,最大线程数必须大于或等于核心线程数,核心线程正在执行任务,而有新任务进来,且当下线程数量未达到最大线程数时,会新建线程放进线程池,再用新线程执行任务。
keepAliveTime,存活时间,除开核心线程外的线程,也就是大于核心线程数的部分线程,如果没有任务调度该线程执行,当该线程的空闲时间达到keepALiveTime值时,会被销毁。
unit,时间单位,keepAliveTime的单位,一般为毫秒
workQueue,工作队列,也称为等待队列,任务调用时先进入工作队列,然后等待空闲线程调度。
threadFactory,线程工厂,指定创建线程的方式,通过线程工厂创建和销毁线程,一般使用 Executors.defaultThreadFactory()
足够,不用自定义创建该工厂类。
handler,拒绝策略,该类主要针对当工作队列满了之后,新的任务进来,拒绝存放在工作队列,执行的操作。其中rejectedExecution方法就是拒绝的处理方法。
ThreadPoolExecutor提供了默认四种拒绝策略。(如果不理解可以看下源码,很简单的逻辑)
在阿里开发手册中明令禁止使用Executors创建线程池,通过以上第一点介绍的6种线程池可以看出,Executors提供的线程池中有些maxinumPoolSize设置为max integer,或者workQueue的容量设置为max integer(如newFixedThreadPool和newSingleThreadExecutor中,workQueue使用LinkedBlockingQueue,其size为Integer.MAX_VALUE
),这存在内存溢出风险。
因而在实际开发中,最好自定义线程池。只需要实例化ThreadPoolExecutor类即可,所以自定义线程池实质上就是配置好上述第二点中的7大参数。
public ThreadPoolExecutor myThreadExecutor(){
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
10,
20,
60,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
return threadPoolExecutor;
}
最后,@Async配合自定义线程池使用,可以看我上篇文章《@Async注解的注意事项》
至此,全篇结束