您的位置:首页 > 编程语言 > Java开发

Java 多线程 - 线程的取消与关闭

2015-04-16 15:32 232 查看
Java没有一种安全的抢占式方法来停止线程,只有一些协作式机制。其中一种协作机制能设置某个“已请求取消”标志,而任务将定期查看该标志。如果设置了这个标志,那么任务将提前结束。举例如下:

[java] view
plaincopyprint?





public class PrimeGenerator implements Runnable {

private static ExecutorService exec = Executors.newCachedThreadPool();

private final List<BigInteger> primes = new ArrayList<BigInteger>();

private volatile boolean cancelled; // 为了保证可靠,需要volatile类型

public void run() {

BigInteger p = BigInteger.ONE;// 创建一个大整数类型,初始值为1

while (!cancelled) {

p = p.nextProbablePrime();

synchronized (this) { // 在添加时要确保同步

primes.add(p);

}

}

}

// 设置取消任务的标识cancelled,以防止搜索素数的线程永远执行下去

public void cancel() {

cancelled = true;

}

// 获取已经计算出来的素数

public synchronized List<BigInteger> get() { // 对ArrayList进行复制,保证正确的遍历

return new ArrayList<BigInteger>(primes);

}

static List<BigInteger> aSecondOfPrimes() throws InterruptedException {

PrimeGenerator generator = new PrimeGenerator();

exec.execute(generator);// 执行这个任务

try {

SECONDS.sleep(1);

} finally {

generator.cancel(); // 确保在调用sleep时被中断也能取消素数生成器的任务

}

return generator.get();

}

}

PrimeGenerator使用了一种简单的取消策略:客户代码通过调用cancel来请求取消,PrimeGenerator在每次搜索素数前首先检查是否存在取消请求,如果存在则退出。

如果使用这种策略来请求取消,那么当任务调用了一个阻塞方法的时候,可能任何永远不会检查取消标志,因此永远不会结束。如下举例:

[java] view
plaincopyprint?





class BrokenPrimeProducer extends Thread {

private final BlockingQueue<BigInteger> queue; // 阻塞队列

private volatile boolean cancelled = false;

BrokenPrimeProducer(BlockingQueue<BigInteger> queue) {

this.queue = queue;

}

public void run() {

try {

BigInteger p = BigInteger.ONE;

while (!cancelled)

queue.put(p = p.nextProbablePrime());

} catch (InterruptedException consumed) {

}

}

public void cancel() {

cancelled = true;

}

}

当生产者将队列添满时,消费者希望取消这个任务。但是由于生产者此时处于阻塞状态,那么cancelled标志将得不到检查,生产者不能从阻塞的方法中恢复过来。

线程中断是一种协作机制,每个线程都有一个boolean类型的中断状态。在Thread类中提供了3个中断方法,如下:

[java] view
plaincopyprint?





public void interrupt(); // 中断目标线程

public boolean isInterrupted(); // 返回目标线程的中断状态

public static boolean interrupted();// 清除当前线程的中断状态,并返回它之前的值。也是清除中断状态的唯一方法

(1)interrupt()方法

对于阻塞库方法,如wait、join、sleep方法,都会检查线程何时中断,并且在发现中断时提前返回。调用这个方法会引起这个线程的interrupt状态被清空(设为false),并且会抛出InterruptedException,表示阻塞操作由于中断而提前结束。

当线程在非阻塞状态下中断时,中断状态将被设置。

(2)isInterrupted()方法

这个方法的源代码如下:

[java] view
plaincopyprint?





public boolean isInterrupted() {

return isInterrupted(false);

}

private native boolean isInterrupted(boolean ClearInterrupted);

返回这个线程是否被interrupt了,调用这个方法不会影响这个线程的interrupt状态

(3)interrupted()方法

来看这个方法的源代码,如下:

[java] view
plaincopyprint?





public static boolean interrupted() {

return currentThread().isInterrupted(true);

}

private native boolean isInterrupted(boolean ClearInterrupted);

调用这个方法会返回当前线程的interrupt状态(true或false),并把当前线程的interrupt状态清空(设为false)。如果在调用的时候返回true,那么除非你想屏蔽这个中断,否则必须对它进行处理 - 可以抛出InterruptedException,或者通过再次调用interrupt()来恢复中断状态。

注意:这个是个静态方法,并且返回的是当前线程状态,并不一定是调用者的线程状态。

使用这几个方法可以解决如上的自定义取消机制与可阻塞的库函数之间交互的问题,如下:

[java] view
plaincopyprint?





public class PrimeProducer extends Thread {

private final BlockingQueue<BigInteger> queue;

PrimeProducer(BlockingQueue<BigInteger> queue) {

this.queue = queue;

}

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();

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: