线程池的3个方法,7大参数,4种拒绝策略
一、3个创建线程池的方法
1. Executors.newSingleThreadExecutor(); // 单个线程的线程池
2. Executors.newFixedThreadPool(5); // 固定大小的线程池
3. Executors.newCachedThreadPool(); // 可伸缩的线程池,遇强则强,遇弱则弱
在阿里开发手册中:
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors 返回的线程池对象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2) CachedThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
以下为三种创建方法的函数的源码:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
以下就是 ThreadPoolExecutor 函数,有7大参数,下节详细阐述。
二、线程池的7大参数
(1) corePoolSize:核心线程数(当等待队列不满的时候,只会调用核心线程)
(2) maximumPoolSize:最大线程数(当等待队列满了的时候,会激活更多的线程)
(3) keepAliveTime:新激活的线程在没有任务的情况下的存活时间
(4) TimeUnit:keepAliveTime 的单位
(5) BlockingQueue:阻塞队列,当核心线程满了,新的请求会在队列中等待
(6) ThreadFactory:创建线程的工厂,使用默认的 Executors.defaultThreadFactory() 即可
(7) RejectedExecutionHandler:拒绝策略,当超出线程池的最大承载时的拒绝策略(有4种拒绝策略)
现在我们再来看看阿里开发手册中的两句话:
1) FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
newSingleThreadExecutor
中的传入的阻塞队列参数为 new LinkedBlockingQueue<Runnable>()
,而 LinkedBlockingQueue
的默认构造函数的队列长度为 Integer.MAX_VALUE 约等于21亿,所以可能会堆积大量的请求,从而导致 OOM。FixedThreadPool
同理。
2) CachedThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
newCachedThreadPool
中的传入的最大线程数参数为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
那么最大线程数应该如何定义呢?分以下两种情况:
- CPU 密集型:几个核心就定义为几,可以保证 CPU 的效率最高,例如一个4核的 CPU,则可以令
maximumPoolSize = 4
,其中可以使用Runtime.getRuntime().availableProcessors()
获取 CPU 的核数。 - IO 密集型:程序中有很多大型任务,IO 十分占用资源!此时要判断程序中十分消耗 IO 的线程数,只要大于该数即可,一般可以设置为两倍。
三、4种拒绝策略
(1) ThreadPoolExecutor.AbortPolicy():当达到线程池的最大承载时,还有请求,则不处理,并且抛出异常。
(2) ThreadPoolExecutor.CallerRunsPolicy(): 当达到线程池的最大承载时,还有请求,那条线程请求那条线程去处理,一般为 main 线程。
(3) ThreadPoolExecutor.DiscardPolicy(): 当达到线程池的最大承载时,还有请求,则丢掉任务,并且不抛出异常。
(4) ThreadPoolExecutor.DiscardOldestPolicy(): 当达到线程池的最大承载时,还有请求,则尝试去和最老的线程竞争, 并且不抛出异常。