- 为什么推荐用ThreadPoolExecutor创建线程池,而不用默认提供的四种线程池
FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
CachedThreadPool 和 ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
所以建议用ThreadPoolExecutor创建线程池,用好线程数与队列长度这两个参数
3. 常用的四种线程池
- newFixedThreadPool 定长线程池
一个有指定的线程数的线程池,有核心的线程,里面有固定的线程数量,响应的速度快。正规的并发线程,多用于服务器。固定的线程数由系统资源设置。核心线程是没有超时机制的,队列大小没有限制,除非线程池关闭了核心线程才会被回收。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- newCachedThreadPool 可缓冲线程池
只有非核心线程,最大线程数很大,每新来一个任务,当没有空余线程的时候就会重新创建一个线程,这边有一个超时机制,当空闲的线程超过60s内没有用到的话,就会被回收,它可以一定程序减少频繁创建/销毁线程,减少系统开销,适用于执行时间短并且数量多的任务场景。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- ScheduledThreadPool 周期线程池
创建一个定长线程池,支持定时及周期性任务执行,通过过schedule方法可以设置任务的周期执行
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
- newSingleThreadExecutor 单任务线程池
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行,每次任务到来后都会进入阻塞队列,然后按指定顺序执行。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
- 线程池的参数及作用
当一个任务提交到线程池,执行过程
提交任务
1 达到核心线程数,判断任务队列是否满了
1.1 队列满了,则判断当前线程数是否满足最大线程数
1.1.1 到达了最大线程数,则根据拒绝策略处理任务
1.1.2 未到达则创建线程执行任务
1.2 没有满,则将任务放入队列中等待任务执行
2 未达到核心线程数,则创建线程完成任务
/**
* @param corePoolSize 除非设置了{@code allowCoreThreadTimeOut},即使它们处于空闲状态也要保留在池中的线程数。
* @param maximumPoolSize 池中允许的最大线程数
* @param keepAliveTime 当线程数大于内核数时,这是多余的空闲线程将在终止之前等待新任务的最长时间。
* @param unit {@code keepAliveTime}参数的时间单位
* @param workQueue 在执行任务之前用于保留任务的队列。此队列将仅保存由{@code execute}方法提交的{@code Runnable}任务。
* @param threadFactory 执行程序创建新线程时要使用的工厂
* @param handler 因为达到了线程界限和队列容量而在执行被阻止时使用的处理程序
*/
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
allowCoreThreadTimeOut 翻译过来就是 允许核心线程数超时
当 allowCoreThreadTimeOut 为true的时候,线程池最后销毁数量为0
当allowCoreThreadTimeOut 为false的时候,当前线程数大于核心线程数,且timeout超时才会销毁
核心线程数:提交一个任务到线程池的时候,线程池会创建一个线程来执行任务,当任务数量超过核心线程数的时候就不会再创建线程了,还有一种情况是执行了 方法,创建线程池的时候默认创建核心线程数数量的线程
最大线程数:当任务超过核心线程数处理的范围,队列也满了就会根据最大线程数创建线程
工作队列:用于保存等待执行的任务的阻塞队列,队列有以下几种
- ArrayBlockingQueue:规定大小的BlockingQueue,其构造必须指定大小。由数组支持的有界阻塞队列,保持先进先出的原则
- LinkedBlockingQueue:大小不固定的BlockingQueue,若其构造时指定大小,生成的BlockingQueue有大小限制,不指定大小,其大小有Integer.MAX_VALUE来决定。基于列表实现,在队列的尾部插入元素,头部获取元素,也是先进先出
- PriorityBlockingQueue:是一个支持优先级的无界阻塞队列,默认是自然的排序,也可以指定元素进行排序
- SynchronizedQueue:从字面理解也就是安全的队列,只能进行某一个操作。
线程工厂:用于设置创建线程的工厂,在我们平常编程中一般不会操作,可通过线程工厂设置每个线程的名字
拒绝策略:当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常,策略有以下几种:
- AbortPolicy
线程池默认的拒绝策略,在任务不能再提交的时候,抛出异常,及时反馈程序运行状态。如果是比较关键的业务,推荐使用此拒绝策略,这样子在系统不能承载更大的并发量的时候,能够及时的通过异常发现。
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
- DiscardPolicy
丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。(一般不建议使用)
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
- DiscardOldestPolicy
丢弃队列最前面的任务,然后重新提交被拒绝的任务
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
- CallerRunsPolicy
如果任务被拒绝了,则由调用线程(提交任务的线程)直接执行此任务,其实也就是主线程执行
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
超时时间单位:TimeUnit:DAYS,HOURS,MINUTES,MILLISECONDS,MICROSECONDS,NANOSECONDS
等待最长时间:线程池的工作线程空闲后,保持存活的时间
-
线程池的状态
Running、ShutDown、Stop、Tidying、Terminated。
-
Running
线程池处于当前状态下可以接受并处理任务,线程池创建后状态就变为了running
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
- ShutDown
线程池处于当前状态的时候,不接受任务,但可以处理已接受的任务
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
- Stop
线程池处于当前状态下,不接受,不处理,还随时中断当前执行的任务
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
- Tidying与Terminated
Terminated 线程池彻底终止
Tidying 当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。
当成为tidying后悔马上执行terminated()方法 但是此方法默认为空,所以会直接进入Terminated 状态
private static int ctlOf(int rs, int wc) { return rs | wc; }
final void tryTerminate() {
for (;;) {
int c = ctl.get();
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated();
} finally {
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}