考虑一个简单的程序:


public static void main(String[] args) throws Exception {
	// Create an ExecutorService with a fixed number of threads: three
	ExecutorService service = Executors.newFixedThreadPool(3);
 
	// Pass a simple Runnable task to the ExecutorService
	service.submit(() -> System.out.println("done"));
}

运行程序会产生我们期望的控制台输出:

完毕

但这并不是我们通常在 IntelliJ IDEA 中看到的输出:

进程结束,退出代码为 0

我们通常会在程序结束时看到。

为什么会这样?

newFixedThreadPool()方法的描述告诉我们,使用ExecutorService创建的线程将继续存在,直到它们被显式停止。这意味着因为我们将任务传递给 ExecutorService 所以创建了一个线程来执行它,并且即使在任务完成后该线程仍然存在。

在 ExecutorService 处停止

因此,我们需要“关闭”(或停止)ExecutorService。我们可以通过两种方式做到这一点:

  1. void shutdown() — 调用此方法后,ExecutorService停止接受新作业。先前提交给ExecutorService 的所有任务将继续运行。

    
    public static void main(String[] args) throws Exception {
    ExecutorService service = Executors.newFixedThreadPool(3);
        	service.submit(() -> System.out.println("task 1"));
        	service.submit(() -> System.out.println("task 2"));
        	service.shutdown();
        	// A RejectedExecutionException will occur here
        	service.submit(() -> System.out.println("task 3"));
    }
    
  2. List<Runnable> shutdownNow() — 此方法尝试停止当前活动的作业。仍在等待轮到他们的任务被丢弃并作为Runnables列表返回。

    
    public static void main(String[] args) throws Exception {
        ExecutorService service = Executors.newFixedThreadPool(5);
        List.of(1, 2, 3, 4, 5, 6, 7, 8).forEach(i -> service.submit(() -> System.out.println(i)));
        List<Runnable> runnables = service.shutdownNow();
        runnables.forEach(System.out::println);
    }
    

输出:

1
2
4
3
java.util.concurrent.FutureTask@1e80bfe8[未完成,任务 = java.util.concurrent.Executors$RunnableAdapter@4edde6e5[包装任务 = Test$$Lambda$16/0x0000000800b95040@70177ecd]]
java.util.concurrent .FutureTask@cc34f4d[未完成,任务 = java.util.concurrent.Executors$RunnableAdapter@66a29884[包装任务 = Test$$Lambda$16/0x0000000800b95040@4769b07b]]
java.util.concurrent.FutureTask@6f539caf[未完成,任务= java.util.concurrent.Executors$RunnableAdapter@17a7cec2[Wrapped task = Test$$Lambda$16/0x0000000800b95040@65b3120a]]
5

进程结束,退出代码为 0

输出将因运行而异。输出中有两种行:

  • 数字表示 ExecutorService设法处理了相应的任务,显示了我们用来创建任务的列表中的数字。

  • 在FutureTask对象上调用toString()方法的结果。这些对象是提交给ExecutorService但未处理的任务。

输出还有另一个有趣的细微差别。在理想情况下,我们首先会看到所有显示的数字,然后是FutureTask对象。但是同步问题使输出中的行混乱。

其他方法

ExecutorService还有几个与停止它相关的方法:

  1. boolean awaitTermination(long timeout, TimeUnit unit) — 此方法阻塞调用它的线程。只要发生以下三个事件中的任何一个,块就会结束:

    • 调用shutdown()方法后,所有活动作业和所有计划任务都已执行;
    • 方法参数确定的超时已经过去;
    • 调用awaitTermination()方法的线程被终止。

    如果ExecutorService在超时结束前停止,则该方法返回true ,如果超时已经结束,则返回 false 。

    
    public static void main(String[] args) throws Exception {
    	ExecutorService service = Executors.newFixedThreadPool(2);
    	service.submit(() -> System.out.println("task 1"));
    	service.submit(() -> System.out.println("task 2"));
    	service.submit(() -> System.out.println("task 3"));
    	service.shutdown();
    	System.out.println(service.awaitTermination(1, TimeUnit.MICROSECONDS));
    }
    
  2. boolean isShutdown() —如果在ExecutorService上调用了shutdown()shutdownNow()方法,则返回true

    
    public static void main(String[] args) throws Exception {
    	ExecutorService service = Executors.newFixedThreadPool(2);
    	service.submit(() -> System.out.println("task 1"));
    	service.submit(() -> System.out.println("task 2"));
    	service.submit(() -> System.out.println("task 3"));
    	System.out.println(service.isShutdown());
    	service.shutdown();
    	System.out.println(service.isShutdown());
    }
    
  3. boolean isTerminated() —如果已在ExecutorService上调用shutdown()shutdownNow()方法并且所有任务都已完成,则返回true 。

    
    public static void main(String[] args) throws Exception {
        ExecutorService service = Executors.newFixedThreadPool(5);
        List.of(1, 2, 3, 4, 5, 6, 7, 8).forEach(i -> service.submit(() -> System.out.println(i)));
        service.shutdownNow();
        System.out.println(service.isTerminated());
    }
    

使用这些方法的示例代码:


public static void main(String[] args) throws Exception {
   ExecutorService service = Executors.newFixedThreadPool(16);
   Callable<String> task = () -> {
       Thread.sleep(1);
       return "Done";
   };
 
   // Add 10,000 tasks to the queue
   List<Future<String>> futures = IntStream.range(0, 10_000)
           .mapToObj(i -> service.submit(task))
           .collect(Collectors.toList());
   System.out.printf("%d tasks were submitted for execution.%n", futures.size());
 
   // Attempt to shut down
   service.shutdown();
   // Wait 100 milliseconds to finish the work
   if (service.awaitTermination(100, TimeUnit.MILLISECONDS)) {
       System.out.println("All tasks completed!");
   } else {
       // Stop forcibly
       List<Runnable> notExecuted = service.shutdownNow();
       System.out.printf("%d tasks were not started.%n", notExecuted.size());
   }
 
   System.out.printf("Total tasks completed: %d.%n", futures.stream().filter(Future::isDone).count());
}

输出(每次运行都不同):

提交了 10,000 个任务以供执行。
9170 任务未启动。
完成的任务总数:830 项任务。

进程结束,退出代码为 0