1. public class SleepMessages {
2. public static void main(String args[])
3. throws InterruptedException {
4. String importantInfo[] = {
5. "Mares eat oats",
6. "Does eat oats",
7. "Little lambs eat ivy",
8. "A kid will eat ivy too"
9. };
10.
11. for (int i = 0;
12. i < importantInfo.length;
13. i++) {
14. //Pause for 4 seconds
15. Thread.sleep(4000);
16. //Print a message
17. System.out.println(importantInfo[i]);
18. }
19. }
20. }
public class SleepMessages {
public static void main(String args[])
throws InterruptedException {
String importantInfo[] = {
"Mares eat oats",
"Does eat oats",
"Little lambs eat ivy",
"A kid will eat ivy too"
};
for (int i = 0;
i < importantInfo.length;
i++) {
//Pause for 4 seconds
Thread.sleep(4000);
//Print a message
System.out.println(importantInfo[i]);
}
}
}
main()方法声明了它有可能抛出InterruptedException。当其他线程中断当前线程时,sleep()方法就会抛出该异常。由于这个应用程序并没有定义其他的线程,所以并不用关心如何处理该异常。
中断(Interrupts)
中断是给线程的一个指示,告诉它应该停止正在做的事并去做其他事情。一个线程究竟要怎么响应中断请求取决于程序员,不过让其终止是很普遍的做法。这是本文重点强调的用法。
一个线程通过调用对被中断线程的Thread对象的interrupt()方法,发送中断信号。为了让中断机制正常工作,被中断的线程必须支持它自己的中断(即要自己处理中断)
中断支持
线程如何支持自身的中断?这取决于它当前正在做什么。如果线程正在频繁调用会抛InterruptedException异常的方法,在捕获异常之后,它只是从run()方法中返回。例如,假设在SleepMessages的例子中,关键的消息循环在线程的Runnable对象的run方法中,代码可能会被修改成下面这样以支持中断:
Java代码
1. for (int i = 0; i < importantInfo.length; i++) {
2. // Pause for 4 seconds
3. try {
4. Thread.sleep(4000);
5. } catch (InterruptedException e) {
6. // We've been interrupted: no more messages.
7. return;
8. }
9. // Print a message
10. System.out.println(importantInfo[i]);
11. }
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。例如:
Java代码
1. for (int i = 0; i < inputs.length; i++) {
2. heavyCrunch(inputs[i]);
3. if (Thread.interrupted()) {
4. // We've been interrupted: no more crunching.
5. return;
6. }
7. }
for (int i = 0; i < inputs.length; i++) {
heavyCrunch(inputs[i]);
if (Thread.interrupted()) {
// We've been interrupted: no more crunching.
return;
}
}
在这个简单的例子中,代码只是检测中断,并在收到中断后退出线程。在更复杂的应用中,抛出一个InterruptedException异常可能更有意义。
Java代码
1. if (Thread.interrupted()){
2. throw new InterruptedException();
3. }
if (Thread.interrupted()){
throw new InterruptedException();
}
这使得中断处理代码能集中在catch语句中。
中断状态标记
中断机制通过使用称为中断状态的内部标记来实现。调用Thread.interrupt()设置这个标记。当线程通过调用静态方法Thread.interrupted()检测中断时,中断状态会被清除。非静态的isInterrupted()方法被线程用来检测其他线程的中断状态,不改变中断状态标记。
按照惯例,任何通过抛出一个InterruptedException异常退出的方法,当抛该异常时会清除中断状态。不过,通过其他的线程调用interrupt()方法,中断状态总是有可能会立即被重新设置。
Joins
Join()方法可以让一个线程等待另一个线程执行完成。若t是一个正在执行的Thread对象,
Java代码
1. t.join();
t.join();
将会使当前线程暂停执行并等待t执行完成。重载的join()方法可以让开发者自定义等待周期。然而,和sleep()方法一样join()方法依赖于操作系统的时间处理机制,你不能假定join()方法将会精确的等待你所定义的时长。
如同sleep()方法,join()方法响应中断并在中断时抛出InterruptedException。
一个简单的线程例子
下面这个简单的例子将会把这一节的一些概念放到一起演示。
SimpleThreads程序有两个线程组成,第一个是主线程,它从创建了一个线程并等待它执行完成。如果MessageLoop线程执行了太长时间,主线程将会将其中断。
MessageLoop现场将会打印一系列的信息。如果中断在它打印完所有信息前发生,它将会打印一个特定的消息并退出。
Java代码
1. public class SimpleThreads {
2.
3. // Display a message, preceded by
4. // the name of the current thread
5. static void threadMessage(String message) {
6. String threadName =
7. Thread.currentThread().getName();
8. System.out.format("%s: %s%n",
9. threadName,
10. message);
11. }
12.
13. private static class MessageLoop
14. implements Runnable {
15. public void run() {
16. String importantInfo[] = {
17. "Mares eat oats",
18. "Does eat oats",
19. "Little lambs eat ivy",
20. "A kid will eat ivy too"
21. };
22. try {
23. for (int i = 0;
24. i < importantInfo.length;
25. i++) {
26. // Pause for 4 seconds
27. Thread.sleep(4000);
28. // Print a message
29. threadMessage(importantInfo[i]);
30. }
31. } catch (InterruptedException e) {
32. threadMessage("I wasn't done!");
33. }
34. }
35. }
36.
37. public static void main(String args[])
38. throws InterruptedException {
39.
40. // Delay, in milliseconds before
41. // we interrupt MessageLoop
42. // thread (default one hour).
43. long patience = 1000 * 60 * 60;
44.
45. // If command line argument
46. // present, gives patience
47. // in seconds.
48. if (args.length > 0) {
49. try {
50. patience = Long.parseLong(args[0]) * 1000;
51. } catch (NumberFormatException e) {
52. System.err.println("Argument must be an integer.");
53. System.exit(1);
54. }
55. }
56.
57. threadMessage("Starting MessageLoop thread");
58. long startTime = System.currentTimeMillis();
59. Thread t = new Thread(new MessageLoop());
60. t.start();
61.
62. threadMessage("Waiting for MessageLoop thread to finish");
63. // loop until MessageLoop
64. // thread exits
65. while (t.isAlive()) {
66. threadMessage("Still waiting...");
67. // Wait maximum of 1 second
68. // for MessageLoop thread
69. // to finish.
70. t.join(1000);
71. if (((System.currentTimeMillis() - startTime) > patience)
72. && t.isAlive()) {
73. threadMessage("Tired of waiting!");
74. t.interrupt();
75. // Shouldn't be long now
76. // -- wait indefinitely
77. t.join();
78. }
79. }
80. threadMessage("Finally!");
81. }
82. }
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!");
}
}