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

Jonathan Komar :

In the book, Java Concurrency in Practice by Brian Goetz et al, the example on page 141 (2006):

7.5: Using interruption for cancellation.

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

The confusing thing is that the book states that Threads should implement an Interruption Policy, while Runnable / Callable tasks should implement a Cancellation Policy.

Yet here we are with a cancel() method inside of a Thread object. What's up with that? A few pages before, an example with Runnable is given (7.1) with cancel(). In the case of tasks, I would expect to see a qualified interrupt() like this:

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

Extra, semi-relevant information

I am using an ExecutorService, so I deal with tasks (not threads--except for a thread factory for the ExecutorService), but I could not find any could examples of a full ExecutorService shutdown (of many threads) in the book.

My methods for starting tasks and stopping them are:

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)?
  }
}
Andrew Tobilko :

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

Right,

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

while Runnable / Callable tasks should implement a Cancellation Policy.

Right,

// 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 is both Thread and Runnable, so both interrupt (to interrupt this thread) and cancel (to cancel this task, the task currently being run by this thread) should be defined.

public class Thread implements Runnable { ... }

The PrimeProducer example is a bit confusing because it assumes the task defined in PrimeProducer will be used outside 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(); }

}

It's very reasonable and accurate since we can do

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

It's rarely the case, though. It's highly likely we would simply go with

new PrimeProducer().start();

which would make the task we define in run context-aware and Thread.currentThread().isInterrupted() and isInterrupted() would mean the same. That's what your confusion over Thread.currentThread().interrupt() and interrupt() comes from.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=140385&siteId=1