Java 并发实践中的 "Using interruption for cancellation" 是否有拼写错误

Is there a typo in "Using interruption for cancellation" in Java Concurrency in Practice

在书中,Java Brian Goetz 等人的并发实践,第 141 页的示例 (2006):

7.5: Using interruption for cancellation.

class PrimeProducer extends Thread {
}
...
public void cancel() { interrupt(); }

令人困惑的是,书中指出线程应实施 中断策略,而可运行/可调用任务应实施取消策略.

然而这里我们在 Thread 对象中使用了一个 cancel() 方法。 这是怎么回事?几页之前,用 cancel() 给出了一个带有 Runnable 的例子 (7.1)。对于任务,我希望看到这样的合格 interrupt()

public void cancel() { Thread.currentThread().interrupt(); }

额外的、半相关的信息

我正在使用 ExecutorService,所以我处理任务(不是线程——ExecutorService 的线程工厂除外),但我找不到任何可能的 ExecutorService 完全关闭(许多线程)的示例这本书。

我启动和停止任务的方法是:

Map<CancellableRunnable, Future<?>> cancellableFutures = new HashMap<>(); // keep track of refs to tasks for stop()
public void init() {
  Future<?> future = myExecutorService.submit(myTask);
  cancellableFutures.put(myTask, future);
}

public void stop() {
  for (Future task : cancellableFutures.values()) {
    task.cancel(true); // also a confusing step. Should it be cancel() on Future or cancel() on task (Runnable/Callable)?
  }
}

要正确关闭一个线程,您必须通过调用 thread.interrupt() 要求它自行关闭并且该线程应定期检查 thread.isInterrupted() 方法。

official documentation 中查看更多详细信息。

对于您的示例,您有一个 ExecutorService myExecutorService。要关闭所有提交的线程(以及 线程池 本身),您可以调用 myExecutorService.shutdown()。结果,线程池为所有线程调用 thread.interrupt()

要仅停止所需的线程,请正确调用 future.cancel(true)。在这种情况下,您的 线程池 将处于活动状态并且能够提交另一个任务。

In the case of tasks, I would expect to see a qualified interrupt() like this:

public void cancel() { Thread.currentThread().interrupt(); }

也就是说中断你自己的线程,而不是线程运行的任务。如果您想让其他事情停止正在做的事情,打断自己是没有意义的:您可以简单地停止正在做的事情。

(您可能会中断当前线程,例如,如果您刚刚捕获了一个 InterruptedException,并希望保留线程被中断的事实。但您不会将此用作一种机制开始中断)。

The confusing thing is that the book states that Threads should implement an Interruption Policy

对,

class MyThread extends Thread {
    @Override
    public void interrupt() { ... }
}

while Runnable / Callable tasks should implement a Cancellation Policy.

对,

// FutureTask = Runnable (for run) + Future<Void> (for cancel(boolean))
class MyTask extends FutureTask<Void> {
    @Override
    public boolean cancel(boolean mayInterruptIfRunning) { ... }

    @Override
    public void run() { ... }
}

Yet here we are with a cancel() method inside of a Thread object.

Thread既是Thread又是Runnable,所以interrupt(中断this线程)和cancel(要取消 this 任务,该线程当前正在 运行 的任务)应该被定义。

public class Thread implements Runnable { ... }

PrimeProducer 示例有点令人困惑,因为它假定 PrimeProducer 中定义的任务将在 PrimeProducer 之外使用。

class PrimeProducer extends Thread {

    public void run() { 
        try {
            BigInteger p = BigInteger.ONE;
            while (!Thread.currentThread().isInterrupted())
                queue.put(p = p.nextProbablePrime()); 
        } catch (InterruptedException consumed) {
            /* Allow thread to exit */
        }
    }

    public void cancel() { interrupt(); }

}

非常合理和准确,因为我们可以做到

Runnable runnable = new PrimeProducer();
new Thread(runnable).start();

不过这种情况很少见。我们很可能会选择

new PrimeProducer().start();

这将使我们在 run 中定义的任务具有上下文感知能力,Thread.currentThread().isInterrupted()isInterrupted() 的含义相同。这就是你对 Thread.currentThread().interrupt()interrupt() 的困惑。