提到 Java 线程池的关闭,绝大多数人第一时间想到的是 ExecutorService
的 shutdown
方法。
关于 shutdown
方法,注释上是这么说明的。
/**
* Initiates an orderly shutdown in which previously submitted
* tasks are executed, but no new tasks will be accepted.
* Invocation has no additional effect if already shut down.
*/
大概意思是,当执行了 shutdown
方法后,线程池将等待已提交任务执行完成后关闭,但在此期间不再接收新的任务。
对此,我们编写一段代码来测试 shutdown
的效果。(注意:这段代码是不完整的,具体原因见下文)
package com.bolt.panda;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.*;
/**
* Created by panda on 2019/2/13.
*/
public class ShutdownDemo {
public static void main(String[] args) {
System.out.println("主线程运行开始!");
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("pool-%d").build();
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(), threadFactory);
executor.submit(new Task());
executor.submit(new Task());
executor.submit(new Task());
executor.shutdown();
System.out.println("主线程运行结束!");
}
}
class Task implements Callable {
@Override
public Object call() throws Exception {
System.out.println("子线程执行中...");
return null;
}
}
输出结果为:
主线程运行开始!
子线程执行中...
主线程运行结束!
子线程执行中...
子线程执行中...
Process finished with exit code 0
可见,线程池确实是关闭了,因为程序退出了。这里需要解释一下,若线程池没有关闭,则会一直等待任务,不会出现程序退出的现象(因为我们设置的核心线程数大于 0
)。
但是,在这里我们发现了一个问题,主线程先于子线程退出了。这一点在很多情况下是不合理的,因为我们常常需要在多线程任务执行结束后回到主线程处理其他工作,例如统计数据等。那么,有什么办法能实现在线程池关闭前主线程保持阻塞状态么?这时候就需要借助 awaitTermination
方法了。
我们先来看看 awaitTermination
的说明:
/**
* Blocks until all tasks have completed execution after a shutdown
* request, or the timeout occurs, or the current thread is
* interrupted, whichever happens first.
*
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
* @return {@code true} if this executor terminated and
* {@code false} if the timeout elapsed before termination
* @throws InterruptedException if interrupted while waiting
*/
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
大体意思是,在主线程调用 shutdown
方法后,若此时再调用 awaitTermination
方法,则主线程会保持阻塞状态,除非发生了以下 3 种情况:
此外,只有当检测到线程池所有任务执行完毕时,awaitTermination
方法才会返回 true
,否则返回 false
。
基于以上,我们可以结合 awaitTermination
实现线程池先于主线程关闭。现在,让我们对之前的代码稍作补充,具体如下:
package com.bolt.panda;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.*;
/**
* Created by panda on 2019/2/13.
*/
public class ShutdownDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程运行开始!");
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("pool-%d").build();
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(), threadFactory);
executor.submit(new Task());
executor.submit(new Task());
executor.submit(new Task());
executor.shutdown();
while (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
System.out.println("检测到仍有活跃子线程,保持阻塞状态...");
}
System.out.println("主线程运行结束!");
}
}
class Task implements Callable {
@Override
public Object call() throws Exception {
System.out.println("子线程执行中...");
return null;
}
}
输出结果为:
主线程运行开始!
子线程执行中...
子线程执行中...
子线程执行中...
主线程运行结束!
Process finished with exit code 0
很明显地看到,主线程不再先于子线程退出,而是在线程池关闭后,再完成最后的收尾工作。