Java并发编程一如何关闭线程池
当线程池中已经有大量线程正在处理任务,并且任务队列中也有很多任务正在等待被处理,这个时候我们该如何去关闭线程池呢?线程池的后续处理又是怎么的呢?
首先我们来创建一个任务。
任务
ShutDownTask
类实现了Runnable
接口,它的run()
就是我们线程所要执行的任务,我们这里的任务非常简单,就是输出自己(当前线程)的名称,再sleep(500)
即可。
class ShutDownTask implements Runnable {
@Override
public void run() {
try {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "被中断了");
}
}
}
shutdown()
我们先来测试一下shutdown()
的作用。
测试代码:
package threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ShutDown {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
executorService.execute(new ShutDownTask());
}
Thread.sleep(1000);
executorService.shutdown();
}
}
输出
pool-1-thread-6
pool-1-thread-5
pool-1-thread-7
pool-1-thread-9
pool-1-thread-10
pool-1-thread-4
pool-1-thread-2
pool-1-thread-8
pool-1-thread-1
pool-1-thread-3
pool-1-thread-5
pool-1-thread-6
pool-1-thread-8
pool-1-thread-10
pool-1-thread-2
我只粘贴了一部分输出,其实会输出100
行,也就是线程池会处理完所有的任务,既包括正在执行的任务,还包括已经提交到任务队列上的任务,我们从代码也可以分析出来,每个任务会sleep(500)
,而在main
线程中提交任务后,只sleep(1000)
,而main
线程sleep(1000)
后,很显然所有的任务是不可能已经被全部执行完的,而我们这里定义的线程池是FixedThreadPool
,它最多只能存在10
个线程去执行任务,所以肯定有任务在任务队列中,而当main
线程调用线程池的shutdown()
后,所有任务依然全部执行完了,可以说明shutdown()
会处理完所有的任务,既包括正在执行的任务,还包括已经提交到任务队列上的任务。
可以得出,调用shutdown()
后线程池的后续操作:
- 停止接收新
submit
的任务。 - 已经提交的任务(包括正在执行的任务和队列中等待的任务),都会继续执行完成。
- 等到第
2
步完成后,才真正停止。
shutdown()后线程池是否还能提交任务?
测试代码:
package threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ShutDown {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
executorService.execute(new ShutDownTask());
}
Thread.sleep(1000);
executorService.shutdown();
executorService.execute(new ShutDownTask());
}
}
很明显是不能再提交任务的,线程池拒绝了提交的任务,之前说过,当线程池满了(不能再创建线程去处理任务,即线程数为最大线程数maximumPoolSize
,任务队列也装满了任务)之后也会拒绝提交的任务,这里又多了一种情况。
如果我们就想让线程池现在、马上、立刻就停止呢?接着看。
shutdownNow()
测试代码:
package threadpool;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ShutDown {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
executorService.execute(new ShutDownTask());
}
Thread.sleep(1000);
List<Runnable> runnableList = executorService.shutdownNow();
}
}
输出:
pool-1-thread-8
pool-1-thread-4
pool-1-thread-2
pool-1-thread-3
pool-1-thread-1
pool-1-thread-6
pool-1-thread-7
pool-1-thread-9
pool-1-thread-10
pool-1-thread-5
pool-1-thread-3
pool-1-thread-1
pool-1-thread-2
pool-1-thread-4
pool-1-thread-8
pool-1-thread-5
pool-1-thread-6
pool-1-thread-10
pool-1-thread-7
pool-1-thread-9
pool-1-thread-2被中断了
pool-1-thread-5被中断了
pool-1-thread-3被中断了
pool-1-thread-6被中断了
pool-1-thread-4被中断了
pool-1-thread-8被中断了
pool-1-thread-1被中断了
这是全部输出了,很显然正在执行的任务会被中断执行,在任务队列中的线程会被直接忽略。
Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution.
翻译:尝试停止所有正在执行的任务,停止处理等待的任务,并返回等待执行任务的列表。
上面代码中的runnableList
就是等待执行任务的列表,以便程序可以做后续处理,如重新创建线程池去处理等。
调用此方法当然也会拒绝新提交的任务,大家可以自己去试一试,要多动手敲代码,代码才会认识你。
可以得出,调用shutdownNow()
后线程池的后续操作:
- 跟
shutdown()
一样,先停止接收新submit
的任务。 - 忽略任务队列里等待的任务。
- 尝试将正在执行的任务
interrupt
中断。 - 返回未执行的任务列表。
线程池试图终止线程的方法是通过调用interrupt()
方法来实现的,这种方法的作用有限,如果线程中没有sleep
、wait
、Condition
、定时锁等应用, interrupt()
方法是无法中断当前的线程的。所以,shutdownNow()
并不代表线程池就一定立即能退出,它也可能必须要等待所有正在执行的任务都执行完成了才能退出。但是大多数时候是能立即退出的。
awaitTermination()
测试代码:
package threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ShutDown {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
executorService.execute(new ShutDownTask());
}
Thread.sleep(1500);
executorService.shutdown();
boolean b = executorService.awaitTermination(1L, TimeUnit.SECONDS);
System.out.println(b);
}
}
结果:
测试代码:
package threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ShutDown {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
executorService.execute(new ShutDownTask());
}
Thread.sleep(1500);
executorService.shutdown();
boolean b = executorService.awaitTermination(7L, TimeUnit.SECONDS);
System.out.println(b);
}
}
结果:
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
*/
根据测试结果与注释我们得出,调用此方法main
线程会阻塞,直到:
- 所有已提交的任务(包括正在执行的任务和队列中等待的任务)执行完。
- 超时时间到了(
timeout
和TimeUnit
设定的时间)。 - 线程被中断,抛出
InterruptedException
。