当线程中断时,为什么 ThreadPool 会中断其工作人员?

Why does ThreadPool interrupts its workers, when thread interrupted?

请看这个例子。我从我的生产项目中获取它。 Web 服务器接收命令并启动新线程,该线程通过 TheadPool 开始计算。当用户想要结束计算时,他发送另一个命令来中断这个新线程,ThreadPool 的工作人员正在关闭。它工作正常,但我不明白为什么。

 public static void main(String[] args) throws Throwable {
        final ExecutorService p = Executors.newFixedThreadPool(2);

        System.out.println("main say: Hello, I'm Main!");
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " say: Starting monitor");
                Thread monitor = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try { 
                            while(true) {
                                Thread.sleep(1500);
                                System.out.println(Thread.currentThread().getName() + " say: I'm still here...hahahahah");
                            }
                        } catch (InterruptedException e) {
                            System.out.println(Thread.currentThread().getName() + " say: Bye for now!");
                        }
                    }
                },"monitor");
                monitor.setDaemon(true);
                monitor.start();

                List<Callable<Integer>> threads = new ArrayList<>();
                for (int i = 0; i < 5; i++) {
                    threads.add(new Callable<Integer>() {
                        @Override
                        public Integer call() throws Exception {
                            System.out.println(Thread.currentThread().getName() + " say: Hello!");
                            try {
                                for (int c = 0; c < 5; c++) {
                                    System.out.println(Thread.currentThread().getName() + " say: " + c);
                                    Thread.sleep(500);
                                }
                            } catch (InterruptedException e) {
                                System.out.println(Thread.currentThread().getName() + " say: I'm interrupted :(");
                            }
                            System.out.println(Thread.currentThread().getName() + " say: Bye!");    
                            return 0;
                        }
                    });
                }
                System.out.println(Thread.currentThread().getName() + " say: Starting workers");
                try {
                    p.invokeAll(threads);
                } catch (InterruptedException e) { 
                    System.out.println(Thread.currentThread().getName() + " say: I'm interrupted :(");
                }
                System.out.println(Thread.currentThread().getName() + " say: Bye!"); 
            }
        }, "new thread");
        System.out.println("main say: Starting new thread");
        t.start();
        System.out.println("main say: Waiting a little...");
        Thread.sleep(1250);
        System.out.println("main say: Interrupting new thread");
        t.interrupt();
    //        p.shutdown();
        System.out.println(String.format("main say: Executor state: isShutdown: %s, isTerminated: %s",
                         p.isShutdown(),
                         p.isTerminated()));
        System.out.println("main say: Bye...");
    }

主要问题:当 currentThread 中断时,为什么 ThreadPool 会中断它的 worker?我在哪里可以了解它的行为?

为什么在这个例子中主线程不退出,但什么都不做? ThreadPool 处于非活动状态但不是 isTerminated 和 isShutdown 并且不处理其余任务。

为什么中断?

ExecutorService.invokeAll() 的 API 中提到了对您的任务的中断:

Throws:

InterruptedException - if interrupted while waiting, in which case unfinished tasks are cancelled

因此,当您在调用 p.invokeAll(threads) 期间收到中断时,threads 中的所有任务都将被取消。

API没有指定if Future.cancel() is called with mayInterruptIfRunning or not, but if you look in the code for AbstractExecutorServiceThreadPoolExecutor继承了invokeAll()的实现,可以看到任务被取消,中断开启:

public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
    throws InterruptedException {
    /* ... */
    try {
        /* ... */
    } finally {
        if (!done)
            for (int i = 0, size = futures.size(); i < size; i++)
                futures.get(i).cancel(true);
    }
}

我想这比不中断地取消它们更有意义,因为已经有一个中断;这是 "just propagating it".

为什么线程池没有完成?

程序不会退出,线程池不会关闭或终止,因为您根本就没有告诉它关闭它。

所以这与下面的简化程序没有什么不同:

public static void main(String[] args) throws Throwable {
    final ExecutorService p = Executors.newFixedThreadPool(2);
    p.execute(new Runnable() { public void run() { } });
    Thread.sleep(1000);
    System.out.println(String.format("main say: Executor state: isShutdown: %s, isTerminated: %s",
                             p.isShutdown(),
                             p.isTerminated()));
}

当您打算关闭它们时,线程池没有任何特殊的魔法可以猜测;他们等到你真正告诉他们。 Executors.newFixedThreadPool() 的文档指出:

The threads in the pool will exist until it is explicitly shutdown.

当您创建线程池时,您需要确保它们最终被清理。通常这是通过调用 shutdown()shutdownNow()。为什么这是必要的?因为 运行 线程在 Java 垃圾回收上下文中是特殊的。 运行 线程是确定哪些对象不会被垃圾回收的起点,并且在它们仍然 运行 时永远不会被垃圾回收。当仍然有 运行 个线程时,Java 程序永远不会退出(当然,除非你调用 System.exit()。)

一些特殊情况,其中线程池可能没有 运行 个线程,因此会被垃圾收集。 API docs for ThreadPoolExecutor 对此进行了解释:

Finalization

A pool that is no longer referenced in a program AND has no remaining threads will be shutdown automatically. If you would like to ensure that unreferenced pools are reclaimed even if users forget to call shutdown(), then you must arrange that unused threads eventually die, by setting appropriate keep-alive times, using a lower bound of zero core threads and/or setting allowCoreThreadTimeOut(boolean).

所以我们可以修改我上面的例子最终这样退出:

final ThreadPoolExecutor p = new ThreadPoolExecutor(
    0, 2, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

或者这个:

final ThreadPoolExecutor p = new ThreadPoolExecutor(
    2, 2, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
p.allowCoreThreadTimeOut(true);

但是当您使用完线程池时调用 shutdownshutdownNow 通常比依赖超时更清晰。

Main question: why does ThreadPool interrupts its workers, when currentThread interrupted? Where can I learn about this its behavior?

你过于笼统了。 ExecutorServiceinvokeAll() 方法在任务中断时取消所有未完成的任务。这记录在 the API docs.

如果您要问 "how would I know it will do that" 那么文档就是您的答案。如果你问为什么接口是这样设计的,那么这是有道理的,因为当它被中断时,该方法抛出 InterruptedException 而不是 returning 一个值,因此可以合理地假设这些未完成的任务可能执行的任何进一步工作都将被浪费。

And why in this example main thread don't exits, but do nothing?

"main thread"是从main()开始的那个。此线程 确实 退出,并且在它退出之前它还做了几件其他事情,包括创建、启动和中断线程,以及输出几条消息。当控制到达 main().

的末尾时退出

但也许你的意思是线程 "new thread" 直接由主线程启动。该线程还做几件事,包括启动监控线程和向执行程序服务提交作业。或者你可能会问为什么这个线程在 ExecutorService 正在工作时不退出,但是为什么它会在等待 invokeAll() 方法到 return 时退出?尽管该方法 return 是 Future 的列表,但它的文档清楚地表明它 阻塞 直到提交给它的所有任务都完成,或者发生异常.