介绍
Java的中断是一种协作机制,也就是说通过中断并不能直接中断另外一个线程,而需要被中断的线程自己处理中断。
在Java的中断模型中,每个线程都有一个boolean标识,代表着是否有中断请求(该请求可以来自所有线程,包括被中断的线程本身)。例如,当线程t1想中断线程t2,只需要在线程t1中将线程t2对象的中断标识置为true,然后线程2可以选择在合适的时候处理该中断请求,甚至可以不理会该请求,就像这个线程没有被中断一样。
中断方法
线程中断相关的方法主要是下面的3方法,他们具体的作用见下面的表格:
方法名 | 介绍 |
void interrupt() | 中断线程,设置线程的中断位true |
boolean isInterrupted() | 检查线程的中断标记位,true-中断状态, false-非中断状态 |
static boolean interrupted() | 静态方法,返回当前线程的中断标记位,同时清除中断标记,改为false。比如当前线程已中断,调用interrupted(),返回true, 同时将当前线程的中断标记位改为false, 再次调用interrupted(),会发现返回false |
记忆方法推荐:
- interrupt()是一个动词,表示中断线程。
- Interrupted是一个形容词,用于检查线程的中断位,而isInterrupted()方法只是简单的检查,interrupted()处理的更加复杂。
可中断的阻塞
针对线程处于由sleep
, wait
, join
,LockSupport.park
等方法调用产生的阻塞状态时,调用interrupt方法,会抛出异常InterruptedException
,同时会清除中断标记位,自动改为false。
不可中断的阻塞
- java.io包中的同步Socket I/O
- java.io包中的同步I/O
- Selector的异步I/O
sychronized
加的锁
实践案例
中断sleep、wait、join等方法
private static void test1() throws InterruptedException { Thread t1 = new Thread(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { log.error("error", e); } }, "t1"); t1.start(); Thread.sleep(500); t1.interrupt(); Thread.sleep(100); log.info(" interrupt status : {}", t1.isInterrupted()); }
结论: 阻塞方法sleep响应中断,抛出InterruptedException
,同时清除中断标记位为false。
中断LockSupport.park方法
public static void test3() throws InterruptedException { Thread t3 = new Thread(() -> { log.debug("t3 park....."); LockSupport.park(); log.debug("t3 unpark....."); log.debug("interrupt status: [{}]", Thread.currentThread().isInterrupted()); log.debug("t3 第二次 park....."); LockSupport.park(); log.debug("t3 中断位为true, park失效....."); }, "t3"); t3.start(); Thread.sleep(1000); t3.interrupt(); }
结论: 阻塞方法park响应中断, 不会抛出异常,同时不会清除中断标记位,任为true。
中断正常方法
private static void test2() throws InterruptedException { Thread t2 = new Thread(() -> { while (true) { boolean isInterrupted = Thread.currentThread().isInterrupted(); if (isInterrupted) { log.info("interrupt status: {}", isInterrupted); break; } } }, "t2"); t2.start(); Thread.sleep(500); t2.interrupt(); Thread.sleep(100); log.info(" thread status, {}, interrupt status : {}", t2.getState(), t2.isInterrupted()); }
结论: 打断正常运行的线程, 不会清空打断状态,同时线程结束后,重置中断状态位。
常见模式
这里提供针对interrupt中断使用的一种常见模式,方便大家在后续的开发过程中使用。
两阶段中止模式
使用场景
在一个线程中“优雅”地停止另一个线程的工作,在停止的时候给另一个线程"料理后事"的机会。比如有个监控线程在做监控,在主线程可以终止他的工作。
代码展示
class MonitorTask { private Thread thread; // 开始监控 public void start() { thread = new Thread(() -> { while (true) { Thread curr = thread.currentThread(); // 如果当前线程是中断状态 if(curr.isInterrupted()) { log.debug("清理资源,料理后事"); break; } try { // 监控执行工作... log.debug("监控执行ing"); Thread.sleep(1000); } catch (InterruptedException e) { log.error("error", e); // 此处中断位被清除,变为false, 重新改为true,进入循环,处理线程后事 curr.interrupt(); } } }, "监控线程"); thread.start(); } // 终止线程 public void stop() { thread.interrupt(); } }
MonitorTask monitorTask = new MonitorTask(); monitorTask.start(); Thread.sleep(1500); monitorTask.stop();