7.1 任务取消
程序清单7-1 使用volatile类型的域来保存取消状态
public class PrimeGenerator implements Runnable{
private final List<BigInteger> primes = new ArrayList<>();
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<>(primes);
}
}
//程序清单7-2 一个仅仅运行一秒钟的素数生成器
public static List<BigInteger> aSecondOfPrimes() throws InterruptedException {
PrimeGenerator generator = new PrimeGenerator();
new Thread(generator).start();
try {
Thread.sleep(1000);
} finally {
generator.cancel();
}
return generator.get();
}
程序清单7-3 不可靠的取消操作把生成者置于阻塞操作中(不要这么做)
/**
* 如果生成者的生成速度大于消费者的消费速度,队列将被填满,put方法将会阻塞。
* 当生产者在put方法中阻塞时,如果消费者希望取消生产者任务,
* 如果用cancel()设置cancelled标志,生产者却永远都不能检查这个标志,因为它无法从阻塞的put方法中恢复过来。
*/
public class BrokenPrimeProductor extends Thread{
private final BlockingQueue<BigInteger> primes;
private volatile boolean cancelled = false;
public BrokenPrimeProductor(BlockingQueue<BigInteger> primes) {
this.primes = primes;
}
@Override
public void run() {
BigInteger p = BigInteger.ONE;
while(!cancelled)
try {
primes.put(p=p.nextProbablePrime());
} catch (InterruptedException e) { }
}
public void cancel() { cancelled = true; }
public boolean needMorePrimes() {
return !cancelled;
}
/**
* 消费者取消生产者的生成状态
* @throws InterruptedException
*/
public void consumePrimes() throws InterruptedException {
BlockingQueue<BigInteger> primes = ...;
BrokenPrimeProductor producer = new BrokenPrimeProductor(primes);
producer.start();
try {
while(needMorePrimes())
consume(primes.take());
} finally {
producer.cancel();
}
}
public void consume(BigInteger prime) {
System.out.println(prime);
}
}
程序清单7-4 Thread的中断方法
public class Thread {
public void interrupt() {//中断...}
public boolean isInterrupted(){//检查是否被中断}
public static boolean interrupted(){//清除当前线程的中断状态,并返回之前的值,这也是清除中断状态的唯一方法}
/*
*阻塞库方法:会检查线程何时中断,发现中断时提前返回
*相应中断时的操作包括清除中断状态,抛出InterruptedException异常
* Object的wait方法同为阻塞方法
*/
public static native void sleep(long millis) throws InterruptedException;
}
程序清单7-5 通过中断来取消
/**
*使用中断而不是BrokenPrimeProductor中的cancled标志来取消
*/
public class PrimeProductor extends Thread{
private final BlockingQueue<BigInteger> primes;
public PrimeProductor(BlockingQueue<BigInteger> primes) {
this.primes = primes;
}
@Override
public void run() {
try {
BigInteger p = BigInteger.ONE;
while(!Thread.currentThread().isInterrupted()) //通过被中断来取消
primes.put(p=p.nextProbablePrime());
} catch (InterruptedException e) {
/*允许线程退出*/
}
}
/**通过中断来取消*/
public void cancel() { interrupt(); }
}
7.1.2 中断策略
7.1.3 相应中断
当调用可中断的阻塞函数时,有2种实用策略可用于处理InterruptedException
-
将InterruptedException传递给调用者
BlockingQueue<BigInteger> queue;
public Task getNextTask() throws InterruptedException {
return queue.take();
}
- 恢复中断状态:从而使调用栈中的上层代码能够对其进行处理
程序清单7-7 不可取消的任务在退出前恢复中断
public Task getNextTask(BlockingQueue<Task> queue) {
boolean isinterrupted = false;
try{
while(true) {
try{
return queue.take();
} catch(InterruptedException e) {
isinterrupted = true;
//重新尝试
}
}
} finally {
if(isinterrupted)
Thread.currentThread().interrupt();
}
}
7.1.4 计时运行
程序清单7-8在外部线程汇总安排中断(不要这样做)
public static final ScheduledExecutorService exec = Executors.newScheduledThreadPool(10);
public static void runTime(Runnable r, long timeOut, TimeUnit unit) {
final Thread taskTread = Thread.currentThread();
exec.schedule(new Runnable() {
public void run() { taskTread.interrupt(); }
}, timeOut, unit);
r.run();
}
程序清单7-9 在专门的线程中中断任务
7.1.5 通过Future来实现取消
程序清单7-10 通过Future来实现取消任务
7.1.6处理不可中断的阻塞
程序清单7-11 通过改写interupt()将非标准的取消操作封装到Thread中
7.1.7通过newTaskFor()封装非标准的取消
程序清单7-12 通过newTaskFor()将非标准的取消操作封装到一个任务中
7.2 停止基于线程的服务
7.2.1 日志服务
7.2.2关闭ExecutorService
7.2.3 “毒丸”对象
7.2.4 只执行一次的服务
7.2.5 shutdownNow的局限性
7.3 处理非正常的线程终止
7.4 JVM关闭
7.4.1关闭钩子
程序清单7-26通过注册一个关闭钩子来停止日志服务
public class JVMHook {
public void start() {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
LogService.this.stop();
} catch(InterruptedException ignored) { }
}
});
}
}