中断
一个中断是指对线程的一个提醒,让它停止做当前的事,转而做一个其他的事情。由程序员决定线程如何应对一个中断,不过很多情况下,时要求线程终止。这节将会强调这个用法。
一个线程通过调用Thread对象的interrupt方法来发送中断。为了让中断机制正确工作,被中断的线程必须要支持它自己的中断。
支持中断
一个线程要如何支持自己的中断呢?这取决于它当前正在做什么。如果这个线程当前正在调用一个会抛出InterruptedException的方法,等它捕获到异常,它只是简单地从run方法中返回。举个例子,假设SleepMessages类中间的信息循环是在Runnable对象的run方法中,那么它需要这样改动,来支持中断。
for (int i = 0; i < importantInfo.length; i++) {
// Pause for 4 seconds
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
// We've been interrupted: no more messages.
return;
}
// Print a message
System.out.println(importantInfo[i]);
}
很多会抛出InterruptedException异常的方法,比如sleep,都被设计成,当收到中断时能立刻返回。
如果一个线程一直没有调用会抛出InterruptedException异常的方法该怎么办?那么它把必须每过一段时间调用一次Thread.interrupted,这个方法会在收到中断之后返回true。举例如下:
for (int i = 0; i < inputs.length; i++) {
heavyCrunch(inputs[i]);
if (Thread.interrupted()) {
// We've been interrupted: no more crunching.
return;
}
}
在上面的简单案例中,代码只是简单地测试了中断,如果线程收到了中断,则退出。在更为复杂的应用中,可能抛出一个InterruptedException会更合理。
if (Thread.interrupted()) {
throw new InterruptedException();
}
这使得中断处理的代码集中在一个catch子句中。
中断状态标记
中断的机制是由被称为中断状态的内部标记实现的。调用Thread.interrupt会设定这个标记。当一个线程调用Thread.interrputed静态方法时,这个标记位就被清零。非静态方法isInterrupted方法,可以被一个线程用来查询另一个线程的中断状态,不会改变中断标志位。
Joins
joins方法允许一个线程等待另一个线程完成。假设t是当前正在执行的线程,则使用t.join()方法会导致当前线程停止运行,直到t的线程结束。对join方法的重载允许程序员指定一个等待时间段。不过,就像sleep方法一样,join方法也依赖于OS的计时,所以你也不能确保join方法一定会等待你指定的时间。
和sleep方法一样,join方法应对中断的方法也是通过InterruptedException来退出。
一个线程的例子
接下来的例子会使用到本节中的所有概念。SimpleThreads类由两个线程构成。第一个线程是每个JAVA应用都有的Main线程。Main线程使用Runnable对象创建了一个新的线程,MessageLoop,然后会等待它的结束。如果MessageLoop线程花了太长时间,主线程会中断它。
MessageLoop线程打印一系列的信息。如果在打印完所有信息之前,它就被中断了,那MessageLoop会打印一条信息并退出。
public class SimpleThreads {
// Display a message, preceded by
// the name of the current thread
static void threadMessage(String message) {
String threadName =
Thread.currentThread().getName();
System.out.format("%s: %s%n",
threadName,
message);
}
private static class MessageLoop
implements Runnable {
public void run() {
String importantInfo[] = {
"Mares eat oats",
"Does eat oats",
"Little lambs eat ivy",
"A kid will eat ivy too"
};
try {
for (int i = 0;
i < importantInfo.length;
i++) {
// Pause for 4 seconds
Thread.sleep(4000);
// Print a message
threadMessage(importantInfo[i]);
}
} catch (InterruptedException e) {
threadMessage("I wasn't done!");
}
}
}
public static void main(String args[])
throws InterruptedException {
// Delay, in milliseconds before
// we interrupt MessageLoop
// thread (default one hour).
long patience = 1000 * 60 * 60;
// If command line argument
// present, gives patience
// in seconds.
if (args.length > 0) {
try {
patience = Long.parseLong(args[0]) * 1000;
} catch (NumberFormatException e) {
System.err.println("Argument must be an integer.");
System.exit(1);
}
}
threadMessage("Starting MessageLoop thread");
long startTime = System.currentTimeMillis();
Thread t = new Thread(new MessageLoop());
t.start();
threadMessage("Waiting for MessageLoop thread to finish");
// loop until MessageLoop
// thread exits
while (t.isAlive()) {
threadMessage("Still waiting...");
// Wait maximum of 1 second
// for MessageLoop thread
// to finish.
t.join(1000);
if (((System.currentTimeMillis() - startTime) > patience)
&& t.isAlive()) {
threadMessage("Tired of waiting!");
t.interrupt();
// Shouldn't be long now
// -- wait indefinitely
t.join();
}
}
threadMessage("Finally!");
}
}
后感
- 上面的例子很有意思,它只是建立了一个新的线程,但是不涉及到任何线程同步的问题。我们常常因为面试题,混淆多线程和线程同步是两个概念,多线程未必需要线程同步,只有在代码会对状态发生改变的时候,我们才会考虑同步的问题。
- 很感谢网络和计算机,给我们这样开发分享知识的环境。
网友评论