本文最后编辑于 前,其中的内容可能需要更新。
何为Java线程中断
Java线程中断是一个大多熟人不太关心的知识点,有的人可能一辈子也用不到,这是一位网络上的大牛说的。
Java线程的中断其实非常简单,说白了就是一个中断标志位的改变。先来看Thread
类3个关于中断的方法:
interrupt():
实例方法,中断该线程;意思就是把中断标志位改为true。
isInterrupted()
实例方法,测试该线程是否被中断;显然,根据中断标志位返回true或false。
Thread.interrupted()
静态方法,测试当前线程是否中断;该方法与isInterrupted()类似,只不过,如果线程发生了中断,则把中断标志清除,也就是改为false。
与中断相关的自然就是中断异常InterruptedException
,当线程执行某些方法时不希望被中断,若被中断则抛出中断异常,如Thread.sleep()方法、Object#wait()方法。
中断线程sleep休眠
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
| public class ThreadInterruptedTest {
public static String currentTime() {
return new SimpleDateFormat("HH:mm:ss").format(new Date()); }
public static void sleep(int second) {
try { Thread.sleep(second * 1000L); } catch (InterruptedException e) { System.out.println("sleep中断异常"); } }
public static void main(String[] args) {
Thread t = new Thread(() -> { System.out.println("线程start - " + currentTime()); sleep(10); System.out.println("线程end - " + currentTime()); }); t.start();
sleep(2); t.interrupt(); } }
|
执行结果
1 2 3
| 线程start - 16:13:53 sleep中断异常 线程end - 16:13:55
|
中断线程因调用Object#wait()方法进入等待的状态
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
| public class ThreadInterruptedTest {
public static String currentTime() {
return new SimpleDateFormat("HH:mm:ss").format(new Date()); } static final Object COMMON = new Object(); public static void waitOfCommon() {
synchronized (COMMON) {
try { COMMON.wait(); } catch (InterruptedException e) { System.out.println("等待COMMON被中断"); } } }
public static void main(String[] args) {
Thread t = new Thread(() -> { System.out.println("线程start - " + currentTime()); waitOfCommon(); System.out.println("线程end - " + currentTime()); }); t.start();
sleep(2); t.interrupt(); } }
|
执行结果
1 2 3
| 线程start - 16:20:43 等待COMMON被中断 线程end - 16:20:45
|
如果不执行中断线程t,由于COMMON没有其它线程调用notify()方法或notifyAll()方法,线程 t 永远不会醒来。
我们知道,线程调用Object#wait()方法时,会释放该对象的锁,当其他线程拿到该对象的锁时,是否可以中断调用Object#wait()方法的线程?
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
| public class ThreadInterruptedTest {
public static String currentTime() {
return new SimpleDateFormat("HH:mm:ss").format(new Date()); }
public static void sleep(int second) {
try { Thread.sleep(second * 1000L); } catch (InterruptedException e) { System.out.println("sleep中断异常"); } }
static final Object COMMON = new Object(); public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (COMMON) { System.out.println("线程1 start - " + currentTime()); try { COMMON.wait(); } catch (InterruptedException e) { System.out.println("线程1 等待COMMON被中断"); } System.out.println("线程1 end - " + currentTime()); } }); t1.start();
sleep(2);
Thread t2 = new Thread(() -> {
synchronized (COMMON) {
System.out.println("线程2 start - " + currentTime()); for (int i = 0; i < 6; i++) { System.out.println("println " + i); sleep(1); } COMMON.notifyAll(); System.out.println("线程2 end - " + currentTime()); }
}); t2.start();
sleep(2); t1.interrupt(); System.out.println("main end"); } }
|
执行结果
1 2 3 4 5 6 7 8 9 10 11 12
| 线程1 start - 16:56:10 线程2 start - 16:56:12 println 0 println 1 main end println 2 println 3 println 4 println 5 线程2 end - 16:56:18 线程1 等待COMMON被中断 线程1 end - 16:56:18
|
可以看出,线程t 在等待状态时,虽然被中断,但此时 COMMON 对象的锁被线程2 持有,所以没有响应中断,当线程2 调用COMMON.notifyAll()方法,线程 1获得 COMMON 对象的锁,发现自己被中断了。
推测线程在调用Object#wait()方法时,并没有立即让出锁,此时锁还在自己手上,但允许其它线程过来抢,直到有其它线程拿走锁后,才真正的失去锁。
所以对于Object#wait()方法,线程只有在获得监视对象的锁时才能响应该方法的中断。
中断线程因调用LockSupport.park()方法进入等待的状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public static void main(String[] args) { Thread t = new Thread(() -> {
System.out.println("线程 start - " + currentTime()); LockSupport.park(); System.out.println("线程 end - " + currentTime()); }); t.start();
sleep(2); t.interrupt(); System.out.println("main end"); }
|
执行结果
1 2 3
| 线程1 start - 17:16:23 main end 线程1 end - 17:16:27
|
当线程调用LockSupport.park()使cpu对其不再进行调度时,可以通过中断的方式唤醒(当然LockSupport.unpark(Thread)也可以),继续执行该线程。
小结
说了这么多,一般开发中线程中断用到的场景少之又少,更多的是出现在线程调度相关的操作中,如AQS,都是大佬们玩的。
似乎线程中断的设计就是为线程调度而设计的。开发中就算强行为了用而用,用其作为线程通信的手段,也会有更好的方案代替。