概述
使用线程时, 我们一般使用new Thread的方式, 如下:
1 2 3 4 5 6 7
| new Thread(new Runnable() {
@Override public void run() { } }).start();
|
这种方式有一些弊端:
- 每次new Thread新建对象性能差。
- 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom
- 缺乏更多功能,如定时执行、定期执行、线程中断
java提供了四种线程池,相较之下有如下好处:
- 重用存在的线程,减少对象创建、消亡的开销,性能佳
- 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞
- 提供定时执行、定期执行、单线程、并发数控制等功能
Callable和Future
创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口。这2种方式的缺陷就是:在执行完任务之后无法获取执行结果。
Callable
Callable也是一个接口,在它里面也只声明了一个方法,只不过这个方法叫做call():
1 2 3
| public interface Callable<V> { V call() throws Exception; }
|
Callable一般情况下是配合ExecutorService来使用的,在ExecutorService接口中声明了若干个submit方法的重载版本:
1 2 3
| <T> Future<T> submit(Callable<T> task); <T> Future<T> submit(Runnable task, T result); Future<?> submit(Runnable task);
|
Future
Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
Future也是一个接口,定义如下
1 2 3 4 5 6 7 8
| public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
|
方法说明:
cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning
表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning
为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning
设置为true,则返回true,若mayInterruptIfRunning
设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning
为true还是false,肯定返回true
isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true
isDone方法表示任务是否已经完成,若任务完成,则返回true
get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回
get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null
FutureTask
因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了FutureTask
1 2 3 4 5
| public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V> { void run(); }
|
可以看出RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
FutureTask提供了2个构造器:
1 2 3 4
| public FutureTask(Callable<V> callable) { } public FutureTask(Runnable runnable, V result) { }
|
线程池实现架构
ThreadPoolExecutor实现了一般的线程池,而ScheduledThreadPoolExecutor继承了ThreadPoolExecutor的实现,然后增加了调度功能。
Executor
Executor,任务的执行者,线程池框架中几乎所有类都直接或者间接实现Executor接口,它是线程池框架的基础。Executor提供了一种将“任务提交”与“任务执行”分离开来的机制,它仅提供了一个Execute()方法用来执行已经提交的Runnable任务。
1 2 3
| public interface Executor { void execute(Runnable command); }
|
ExecutorService
ExecutorService提供了将任务提交给执行者的接口(submit方法),让执行者执行任务(invokeAll, invokeAny方法)的接口等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
|
AbstractExecutorService
为ExecutorService的默认实现,AbstractExecutorService除了实现ExecutorService接口外,还提供了newTaskFor()方法返回一个RunnableFuture,在运行的时候,它将调用底层可调用任务,作为 Future 任务,它将生成可调用的结果作为其结果,并为底层任务提供取消操作
ScheduledExecutorService
继承ExecutorService,为一个“延迟”和“定期执行”的ExecutorService。他提供了一些如下几个方法安排任务在给定的延时执行或者周期性执行:
1 2 3 4 5 6 7 8 9 10 11 12
| <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)
ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
|
ThreadPoolExecutor
ThreadPoolExecutor
继承于抽象类AbstractExecutorService
, 是线程池的主要实现类
线程池实现原理
线程池处理流程
excute流程:
如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。
如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue。
如果无法将任务加入BlockingQueue(队列已满),则在非corePool中创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。
如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用
RejectedExecutionHandler.rejectedExecution()方法。
线程池饱和拒绝策略
AbortPolicy: 为java线程池默认的阻塞策略,不执行此任务,而且直接抛出一个运行时异常,切记ThreadPoolExecutor.execute需要try catch,否则程序会直接退出
DiscardPolicy: 不做任务处理,直接忽略这个任务
DiscardOldestPolicy:如果线程池没有被关闭的话,把队列队头的任务(也就是等待了最长时间的)直接扔掉,然后提交这个任务到等待队列中
CallerRunsPolicy: 只要线程池没有被关闭,那么由提交任务的线程自己来执行这个任务
用户自定义拒绝策略(最常用):实现RejectedExecutionHandler
,并自己定义策略模式
源码分析
ctl变量
ThreadPoolExcuter是将两个内部值打包成一个值,即将workerCount和runState(运行状态)这两个值打包在一个ctl中,因为runState有5个值,需要3位,所以有3位表示runState,而其他29位表示为workerCount。而运行时要获取其他数据时,只需要对ctl进行拆包即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); private static final int COUNT_BITS = Integer.SIZE - 3; private static final int CAPACITY = (1 << COUNT_BITS) - 1;
private static final int RUNNING = -1 << COUNT_BITS; private static final int SHUTDOWN = 0 << COUNT_BITS; private static final int STOP = 1 << COUNT_BITS; private static final int TIDYING = 2 << COUNT_BITS; private static final int TERMINATED = 3 << COUNT_BITS;
private static int runStateOf(int c) { return c & ~CAPACITY; } private static int workerCountOf(int c) { return c & CAPACITY; } private static int ctlOf(int rs, int wc) { return rs | wc; }
|
关键的类成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| private final BlockingQueue<Runnable> workQueue;
private final HashSet<Worker> workers = new HashSet<Worker>();
private volatile ThreadFactory threadFactory;
private volatile RejectedExecutionHandler handler;
1、CallerRunsPolicy:在调用者线程里面运行该任务 2、DiscardPolicy:丢弃任务 3、DiscardOldestPolicy:丢弃workQueue的头部任务
private volatile int corePoolSize;
private volatile int maximumPoolSize;
|
execute流程
execute源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); }
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) reject(command); else if (workerCountOf(recheck) == 0) addWorker(null, false); } else if (!addWorker(command, false)) reject(command); }
|
addWorker流程
addWorker源码流程:
- 检查是否可以根据当前池状态和给定的边界(核心或最大)
- 添加新工作线程。如果是这样,工作线程数量会相应调整,如果可能的话,一个新的工作线程创建并启动
- 将firstTask作为其运行的第一项任务。
- 如果池已停止此方法返回false
- 如果线程工厂在被访问时未能创建线程,也返回false
- 如果线程创建失败,或者是由于线程工厂返回null,或者由于异常(通常是在调用Thread.start()后的OOM)),我们干净地回滚
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
| private boolean addWorker(Runnable firstTask, boolean core) { retry: for (;;) { int c = ctl.get(); int rs = runStateOf(c); if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty())) return false; for (;;) { int wc = workerCountOf(c); if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false; if (compareAndIncrementWorkerCount(c)) break retry; c = ctl.get(); if (runStateOf(c) != rs) continue retry; } }
boolean workerStarted = false; boolean workerAdded = false; Worker w = null; try { final ReentrantLock mainLock = this.mainLock; w = new Worker(firstTask); final Thread t = w.thread; if (t != null) { mainLock.lock(); try { int c = ctl.get(); int rs = runStateOf(c); if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { if (t.isAlive()) throw new IllegalThreadStateException(); workers.add(w); int s = workers.size(); if (s > largestPoolSize) largestPoolSize = s; workerAdded = true; } } finally { mainLock.unlock(); } if (workerAdded) { t.start(); workerStarted = true; } } } finally { if (! workerStarted) addWorkerFailed(w); } return workerStarted; }
|
工作线程-runWorker流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
|
final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null; w.unlock(); boolean completedAbruptly = true; try { while (task != null || (task = getTask()) != null) { w.lock(); if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()) wt.interrupt(); try { beforeExecute(wt, task); Throwable thrown = null; try { task.run(); } catch (RuntimeException x) { thrown = x; throw x; } catch (Error x) { thrown = x; throw x; } catch (Throwable x) { thrown = x; throw new Error(x); } finally { afterExecute(task, thrown); } } finally { task = null; w.completedTasks++; w.unlock(); } } completedAbruptly = false; } finally { processWorkerExit(w, completedAbruptly); } }
|
getTask获取任务流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
|
private Runnable getTask() { boolean timedOut = false;
for (;;) { int c = ctl.get(); int rs = runStateOf(c);
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { decrementWorkerCount(); return null; }
int wc = workerCountOf(c);
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) return null; continue; }
try { Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r; timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } }
|
processWorkerExit
执行线程回收处理, 走到这里说明:
- 说明 getTask 返回 null,也就是说,队列中已经没有任务需要执行了,执行关闭
- 任务执行过程中发生了异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| private void processWorkerExit(Worker w, boolean completedAbruptly) { if (completedAbruptly) decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { completedTaskCount += w.completedTasks; workers.remove(w); } finally { mainLock.unlock(); } tryTerminate();
int c = ctl.get(); if (runStateLessThan(c, STOP)) { if (!completedAbruptly) { int min = allowCoreThreadTimeOut ? 0 : corePoolSize; if (min == 0 && ! workQueue.isEmpty()) min = 1; if (workerCountOf(c) >= min) return; } addWorker(null, false); } }
|
Worker执行流程:
线程池的使用
创建线程池的参数
我们可以通过ThreadPoolExecutor来创建一个线程池:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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; }
|
各个参数含义:
corePoolSize: 线程池基本大小,核心线程池大小,活动线程小于corePoolSize则直接创建,大于等于则先加到workQueue中,队列满了才创建新的线程。当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。
maximumPoolSize:最大线程数,超过就reject;线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。
keepAliveTime:线程池的工作线程空闲后,保持存活的时间。所以,如果任务很多,并且每个任务执行的时间比较短,可以调大时间,提高线程的利用率(创建线程的开销比较大)
unit:线程活动保持时间的单位)
workQueue:工作队列,线程池中的工作线程都是从这个工作队列源源不断的获取任务进行执行
threadFactory:用于生成线程,一般我们可以用默认的就可以了。通常,我们可以通过它将我们的线程的名字设置得比较可读一些,如 Message-Thread-1, Message-Thread-2 类似这样
handler: 线程池饱和策略。当线程池已经满了,但是又有新的任务提交的时候,该采取什么策略由这个来指定
线程池拒绝策略
也就是上面的 handler , 当线程池饱和后,对新任务采取的拒绝策略。
AbortPolicy: 默认拒绝策略, 直接抛出 RejectedExecutionException
, 源码如下:
1 2 3 4 5 6 7 8
| 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()); } }
|
CallerRunsPolicy: 只要线程池没有被关闭,那么由提交任务的线程自己来执行这个任务:
1 2 3 4 5 6 7 8
| public static class CallerRunsPolicy implements RejectedExecutionHandler { public CallerRunsPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { r.run(); } } }
|
DiscardPolicy: 什么也不做,忽略这个任务 :
1 2 3 4 5
| public static class DiscardPolicy implements RejectedExecutionHandler { public DiscardPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { } }
|
DiscardOldestPolicy: 这个策略相对霸道一些,如果线程池没有被关闭的话, 把队列队头的任务(也就是等待了最长时间的)直接扔掉,然后提交这个任务到等待队列中
1 2 3 4 5 6 7 8 9
| public static class DiscardOldestPolicy implements RejectedExecutionHandler { public DiscardOldestPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { e.getQueue().poll(); e.execute(r); } } }
|
固定线程池 FixedThreadPool
1 2 3 4 5
| public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
|
最大线程数设置为与核心线程数相等,此时 keepAliveTime 设置为 0(因为这里它是没用的,即使不为 0,线程池默认也不会回收 corePoolSize 内的线程),任务队列采用 LinkedBlockingQueue
,无界队列.
刚开始,每提交一个任务都创建一个 worker,当 worker 的数量达到 nThreads 后,不再创建新的线程,而是把任务提交到 LinkedBlockingQueue 中,而且之后线程数始终为nThreads。如果任务过多,很容易导致内存飙升甚至oom
复用线程池 CachedThreadPool
1 2 3 4 5
| public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
|
核心线程数为 0,最大线程数为 Integer.MAX_VALUE,keepAliveTime 为 60 秒,任务队列采用 SynchronousQueue。
这种线程池对于任务可以比较快速地完成的情况有比较好的性能。如果线程空闲了 60 秒都没有任务,那么将关闭此线程并从线程池中移除。所以如果线程池空闲了很长时间也不会有问题,因为随着所有的线程都会被关闭,整个线程池不会占用任何的系统资源。
SynchronousQueue 是一个比较特殊的 BlockingQueue,其本身不储存任何元素,它有一个虚拟队列(或虚拟栈),不管读操作还是写操作,如果当前队列中存储的是与当前操作相同模式的线程,那么当前操作也进入队列中等待;如果是相反模式,则配对成功,从当前队列中取队头节点
合理的配置线程池
要想合理地配置线程池,就必须首先分析任务特性,可以从以下几个角度来分析:
- 任务的性质:CPU密集型任务、IO密集型任务和混合型任务
性质不同的任务可以用不同规模的线程池分开处理。CPU密集型任务应配置尽可能小的线程,如配置Ncpu+1
个线程的线程池。由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如2*Ncpu
。混合型的任务,如果可以拆分,将其拆分成一个CPU密集型任务和一个IO密集型任务,只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐量将高于串行执行的吞吐量。如果这两个任务执行时间相差太大,则没必要进行分解。可以通过Runtime.getRuntime().availableProcessors()
方法获得当前设备的CPU个数。
- 服务器IO性能优化公式:
最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目
, 可以优化成:
最佳线程数目 = (线程等待时间/线程CPU时间之比 + 1)* CPU数目
比如平均每个线程CPU运行时间为0.5s,而线程等待时间(非CPU运行时间,比如IO)为1.5s,CPU核心数为8,那么根据上面这个公式估算得到:((1.5/0.5 + 1)*8=32
关闭线程池
- RUNNING:线程池创建之后的初始状态,这种状态下可以执行任务。
- SHUTDOWN:该状态下线程池不再接受新任务,但是会将工作队列中的任务执行结束。
- STOP: 该状态下线程池不再接受新任务,但是不会处理工作队列中的任务,并且将会中断线程。
- TIDYING:该状态下所有任务都已终止,将会执行 terminated() 钩子方法。
- TERMINATED:执行完 terminated() 钩子方法之后。
shutdown
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public void shutdown() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { checkShutdownAccess(); advanceRunState(SHUTDOWN); interruptIdleWorkers(); onShutdown(); } finally { mainLock.unlock(); } tryTerminate(); }
|
这里先加锁,然后检测状态,紧接着线程池状态就会变成SHUTDOWN
, 线程池将不在接收任何任务。此时如果还继续往线程池提交任务,将会使用线程池拒绝策略响应,默认情况下将会使用 ThreadPoolExecutor.AbortPolicy
,抛出 RejectedExecutionException
异常
interruptIdleWorkers
方法只会中断空闲的线程,不会中断正在执行任务的的线程。空闲的线程将会阻塞在线程池的阻塞队列上
shutdownNow
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 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; }
|
相较于 shutdown, shutdownNow 会直接中断工作线程。但是中断线程并不代表线程立刻结束。这里需要线程主动配合线程中断响应(线程 interrupt 机制)。
awaitTermination
线程池 shutdown 与 shutdownNow 方法都不会主动等待执行任务的结束,如果需要等到线程池任务执行结束,需要调用 awaitTermination 主动等待任务调用结束。
使用方法如下:
1 2 3 4 5 6 7 8 9
| threadPool.shutdown(); try { while (!threadPool.awaitTermination(60,TimeUnit.SECONDS)){ System.out.println("线程池任务还未执行结束"); } } catch (InterruptedException e) { e.printStackTrace(); }
|
如果线程池任务执行结束,awaitTermination 方法将会返回 true,否则当等待时间超过指定时间后将会返回 false
更加优雅的关闭线程池
回顾上面线程池状态关系图,我们可以知道处于 SHUTDOWN 的状态下的线程池依旧可以调用 shutdownNow。所以我们可以结合 shutdown , shutdownNow,awaitTermination ,更加优雅关闭线程池:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| threadPool.shutdown();
try { if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) { threadPool.shutdownNow(); if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) System.err.println("线程池任务未正常执行结束"); } } catch (InterruptedException ie) { threadPool.shutdownNow(); }
|
参考资料