原文:https://www.ibm.com/developerworks/cn/java/j-jtp05236.html
这样的情景您也许并不陌生:您在编写一个测试程序,程序需要暂停一段时间,于是调用Thread.sleep()
InterruptedException
。InterruptedException
InterruptedException
清单 4
阻塞方法
InterruptedException
阻塞(blocking)方法,如果您响应得当的话,它将尝试消除阻塞并尽早返回。
阻塞方法不同于一般的要运行较长时间的方法。一般方法的完成只取决于它所要做的事情,以及是否有足够多可用的计算资源(CPU 周期和内存)。而阻塞方法的完成还取决于一些外部的事件,例如计时器到期,I/O 完成,或者另一个线程的动作(释放一个锁,设置一个标志,或者将一个任务放在一个工作队列中)。一般方法在它们的工作做完后即可结束,而阻塞方法较难于预测,因为它们取决于外部事件。阻塞方法可能影响响应能力,因为难于预测它们何时会结束。
阻塞方法可能因为等不到所等的事件而无法终止,因此令阻塞方法可取消Thread
Thread.sleep()
Object.wait()
InterruptedException
InterruptedException
InterruptedException
,以便能够用于可取消活动中,而不至于影响响应。
线程中断
每个线程都有一个与之相关联的 Boolean 属性,用于表示线程的中断状态(interrupted status)Thread.interrupt()
Thread.sleep()
Thread.join()
Object.wait()
InterruptedException
interrupt()
Thread.isInterrupted()
Thread.interrupted()
Thread.sleep()
,很认真地对待这样的请求,但每个方法不是一定要对中断作出响应。对于中断请求,不阻塞但是仍然要花较长时间执行的方法可以轮询中断状态,并在被中断的时候提前返回。 您可以随意忽略中断请求,但是这样做的话会影响响应。
中断的协作特性所带来的一个好处是,它为安全地构造可取消活动提供更大的灵活性。我们很少希望一个活动立即停止;如果活动在正在进行更新的时候被取消,那么程序数据结构可能处于不一致状态。中断允许一个可取消活动来清理正在进行的工作,恢复不变量,通知其他活动它要被取消,然后才终止。
处理 InterruptedException
InterruptedException
InterruptedException
InterruptedException
putTask()
getTask()
InterruptedException
清单 1. 不捕捉 InterruptedException,将它传播给调用者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class TaskQueue {
private static final int MAX_TASKS = 1000;
private BlockingQueue< Task > queue
= new LinkedBlockingQueue< Task >(MAX_TASKS);
public void putTask(Task r) throws InterruptedException {
queue.put(r);
}
public Task getTask() throws InterruptedException {
return queue.take();
} } |
InterruptedException
,执行清理,然后抛出异常。清单 2 演示了这种技术,该代码是用于匹配在线游戏服务中的玩家的一种机制。matchPlayers()
InterruptedException
,这样那个玩家对游戏的请求就不至于丢失。
清单 2. 在重新抛出 InterruptedException 之前执行特定于任务的清理工作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public class PlayerMatcher {
private PlayerSource players;
public PlayerMatcher(PlayerSource players) {
this.players = players;
}
public void matchPlayers() throws InterruptedException {
try {
Player playerOne, playerTwo;
while (true) {
playerOne = playerTwo = null;
// Wait for two players to arrive and start a new game
playerOne = players.waitForPlayer(); // could throw IE
playerTwo = players.waitForPlayer(); // could throw IE
startNewGame(playerOne, playerTwo);
}
}
// If we got one player and were interrupted, put that player back
if (playerOne != null)
players.addFirst(playerOne);
// Then propagate the exception
throw e;
}
} } |
不要生吞中断
InterruptedException
Runnable
InterruptedException
InterruptedException
InterruptedException
interrupt()
InterruptedException
清单 3. 捕捉 InterruptedException 后恢复中断状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class TaskRunner implements Runnable {
private BlockingQueue< Task > queue;
public TaskRunner(BlockingQueue< Task > queue) {
this.queue = queue;
}
public void run() {
try {
while (true) {
Task task = queue.take(10, TimeUnit.SECONDS);
task.execute();
}
}
catch (InterruptedException e) {
// Restore the interrupted status
Thread.currentThread().interrupt();
}
} } |
InterruptedException
InterruptedException
清单 4. 生吞中断 ―― 不要这么做
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // Don't do this public class TaskRunner implements Runnable {
private BlockingQueue< Task > queue;
public TaskRunner(BlockingQueue< Task > queue) {
this.queue = queue;
}
public void run() {
try {
while (true) {
Task task = queue.take(10, TimeUnit.SECONDS);
task.execute();
}
}
catch (InterruptedException swallowed) {
/* DON'T DO THIS - RESTORE THE INTERRUPTED STATUS INSTEAD */
}
} } |
InterruptedException
,不管您是否计划处理中断请求,仍然需要重新中断当前线程,因为一个中断请求可能有多个 “接收者”。标准线程池 (ThreadPoolExecutor
)worker 线程实现负责中断,因此中断一个运行在线程池中的任务可以起到双重效果,一是取消任务,二是通知执行线程线程池正要关闭。如果任务生吞中断请求,则 worker 线程将不知道有一个被请求的中断,从而耽误应用程序或服务的关闭。
实现可取消任务
语言规范中并没有为中断提供特定的语义,但是在较大的程序中,难于维护除取消外的任何中断语义。取决于是什么活动,用户可以通过一个 GUI 或通过网络机制,例如 JMX 或 Web 服务来请求取消。程序逻辑也可以请求取消。例如,一个 Web 爬行器(crawler)如果检测到磁盘已满,它会自动关闭自己,否则一个并行算法会启动多个线程来搜索解决方案空间的不同区域,一旦其中一个线程找到一个解决方案,就取消那些线程。
仅仅因为一个任务是可取消的,并不意味着需要立即Thread.isInterrupted()
InterruptedException
。
Thread
Runnable
isInterrupted()
BlockingQueue.put()
。
清单 5. 如果知道线程正要退出的话,则可以生吞中断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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(); } } |
不可中断的阻塞方法
InterruptedException
InterruptedException
SocketException
。java.nio
Selector
synchronized
ReentrantLock
不可取消的任务
BlockingQueue.take()
InterruptedException
。)
清单 6. 在返回前恢复中断状态的不可取消任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public Task getNextTask(BlockingQueue< Task > queue) {
boolean interrupted = false;
try {
while (true) {
try {
return queue.take();
} catch (InterruptedException e) {
interrupted = true;
// fall through and retry
}
}
} finally {
if (interrupted)
Thread.currentThread().interrupt();
} } |
结束语
InterruptedException
相关主题
- 英文原文。
- Java 2 Platform API Specifications:用于 Java 2 Platform, Standard Edition, version 1.4.2 的 API 规范。
- Java Concurrency in Practice(Addison-Wesley Professional,作者 Brian Goetz、Tim Peierls、Joshua Bloch、Joseph Bowbeer、David Holmes 和 Doug Lea,2006 年 6 月):这个关于用 Java 语言开发并发程序的入门手册的第 7 章更深入地谈到了中断和取消的细节。
- JDK 5.0 Update 6:获得最新的 JDK 5.0 更新。
- The IBM implementation of Java technology:下载 SDKs for AIX, Linux, and z/OS,同时还可以从该页面下载 Java 技术的其他 IBM 开发人员工具包。
- Java 技术专区:这里有数百篇关于 Java 编程各方面的文章。