使用 volatile 类型的域来保存取消状态
public class PrimeGenerator implements Runnable {
private final List<BigInteger> primes = new ArrayList<BigInteger>();
private volatile boolean cancelled;
@Override
public void run() {
BigInteger p = BigInteger.ONE;
while(!cancelled) {
p = p.nextProbablePrime();
synchronized (this) {
primes.add(p);
}
}
}
public void cancel() {
cancelled = true;
}
public synchronized List<BigInteger> get(){
return new ArrayList<BigInteger>(primes);
}
}
PrimeGenerator中的取消机制最终会使得搜索素数的任务退出,但在推出过程中需要花费一定的时间。然而,如果使用这种方法的任务调用了一个阻塞方法,例如 BlockingQueue.put,那么可能会产生一个更严重的问题——任务可能永远不会检查取消标志,因此永远不会结束。请看下面案例:
不可靠的取消操作将把生产者置于阻塞的操作中(不要这么做)
public class BrokenPrimeProducer extends Thread {
private final BlockingQueue<BigInteger> queue;
private volatile boolean cancelled = false;
public BrokenPrimeProducer(BlockingQueue<BigInteger> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
BigInteger p = BigInteger.ONE;
while(!cancelled) {
queue.put(p = p.nextProbablePrime());
}
} catch (InterruptedException e) {}
}
public void cancel() {
cancelled = true;
}
void consumerPrimes() throws InterruptedException {
BlockingQueue<BigInteger> primes = new LinkedBlockingQueue<BigInteger>(10);
BrokenPrimeProducer producer = new BrokenPrimeProducer(primes);
producer.start();
try {
while(needMorePrimes())
consume(primes.take());
} finally {
producer.cancel();
}
}
}
相比而言一些阻塞库的方法支持中断。线程中断时一种协作机制,线程可以通过这种机制来通知另一个线程,告诉它在合适的或者可能的情况下停止当前工作,并转而执行其他的工作。
阻塞库方法,例如Thread.sleep 和 Object.wait 等,都会检查线程何时中断,并且在发现中断时提前返回。它们在响应中断时执行的操作包括:清除中断状态,抛出 InterruptedException,表示阻塞操作由于中断而提前结束。JVM并不能保证阻塞方法检测到中断的速度,但在实际情况中响应速度还是非常快的。