-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathThreadInterruption.java
More file actions
156 lines (144 loc) · 5.7 KB
/
Copy pathThreadInterruption.java
File metadata and controls
156 lines (144 loc) · 5.7 KB
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package Phase7_Concurrency.Multithreading;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.locks.LockSupport;
/**
* Thread Interruption
* -------------------
* Java has NO safe way to forcefully stop another thread. Instead it has
* a COOPERATIVE cancellation mechanism: the INTERRUPT FLAG.
* <p>
*
* The Three Calls
* ---------------
* t.interrupt() - set the interrupt flag on thread t.
* If t is currently in a blocking call
* (sleep, wait, join, park, etc.) it
* throws InterruptedException AND the
* flag is CLEARED.
* <p>
*
* t.isInterrupted() - read t's flag without changing it.
* <p>
*
* Thread.interrupted() - STATIC. Read AND CLEAR the current
* thread's flag. Use sparingly — you
* often want to keep the flag set.
* <p>
*
* What gets interrupted
* ---------------------
* Built-in interruptible operations (throw InterruptedException):
* Thread.sleep / join / wait
* Object.wait
* BlockingQueue.put / take / offer-with-timeout
* CountDownLatch.await
* CyclicBarrier.await
* Semaphore.acquire
* Lock.lockInterruptibly / Condition.await
* Future.get
* LockSupport.park* (sets the flag; does NOT throw)
* <p>
*
* Plain compute loops are NOT interrupted automatically — you must
* check the flag yourself with isInterrupted() / Thread.interrupted().
* <p>
*
* Best Practices
* --------------
* 1. NEVER swallow InterruptedException silently. Either:
* a) Restore the flag: Thread.currentThread().interrupt();
* b) Wrap and rethrow as a domain exception.
* A library that hides the interrupt makes its callers' shutdown
* logic impossible.
* <p>
*
* 2. In compute loops, sprinkle:
* if (Thread.currentThread().isInterrupted()) break; // exit cleanly
* <p>
*
* 3. Prefer interruption over a custom "stop" boolean — it integrates
* with every JDK blocking primitive for free.
* <p>
*
* 4. interrupt() is the modern replacement for the deprecated
* Thread.stop(). Forget Thread.stop ever existed.
*/
public class ThreadInterruption {
public static void main(String[] args) throws InterruptedException {
section("1) Interrupting a sleeping thread");
Thread sleeper = new Thread(() -> {
try {
Thread.sleep(5_000);
System.out.println("woke naturally");
} catch (InterruptedException ie) {
System.out.println("sleeper interrupted; flag now = " +
Thread.currentThread().isInterrupted());
// Restore the flag so callers up the stack notice:
Thread.currentThread().interrupt();
}
}, "sleeper");
sleeper.start();
Thread.sleep(50);
sleeper.interrupt();
sleeper.join();
System.out.println("flag after death = " + sleeper.isInterrupted()); // false (TERMINATED)
section("2) Interrupting a compute loop — must CHECK the flag");
Thread compute = new Thread(() -> {
long count = 0;
while (!Thread.currentThread().isInterrupted()) {
count++;
if (count % 1_000_000 == 0) System.out.println(" still going, count=" + count);
}
System.out.println("compute exited cleanly, count=" + count);
}, "compute");
compute.start();
Thread.sleep(50);
compute.interrupt();
compute.join();
section("3) Interrupting BlockingQueue.take()");
BlockingQueue<Integer> q = new ArrayBlockingQueue<>(1);
Thread consumer = new Thread(() -> {
try {
Integer v = q.take(); // will block forever
System.out.println("got " + v);
} catch (InterruptedException ie) {
System.out.println("take() interrupted");
Thread.currentThread().interrupt();
}
}, "consumer");
consumer.start();
Thread.sleep(50);
consumer.interrupt();
consumer.join();
section("4) Interrupted vs isInterrupted (static clears, instance does not)");
Thread.currentThread().interrupt();
System.out.println("isInterrupted (no clear) = " + Thread.currentThread().isInterrupted());
System.out.println("interrupted (clears) = " + Thread.interrupted());
System.out.println("isInterrupted now = " + Thread.currentThread().isInterrupted());
section("5) LockSupport.park sets the flag but does NOT throw");
Thread parker = new Thread(() -> {
LockSupport.park();
System.out.println("parker unparked; flag = " + Thread.currentThread().isInterrupted());
}, "parker");
parker.start();
Thread.sleep(50);
parker.interrupt(); // wakes park() and sets flag
parker.join();
section("6) Worst practice — swallowing interrupts");
// Don't do this:
try { Thread.sleep(1); }
catch (InterruptedException ie) { /* swallowed */ }
// Better:
try { Thread.sleep(1); }
catch (InterruptedException ie) {
Thread.currentThread().interrupt();
System.out.println("flag restored = " + Thread.currentThread().isInterrupted());
}
section("done");
}
private static void section(String title) {
System.out.println();
System.out.println("=== " + title + " ===");
}
}