##1 为什么有这三个函数的存在
由于stop关闭线程的不安全性等各种缺点,JDK提供了合理方式便是这三种函数的组合使用
##2 三个函数申明和定义
-
interrupt方法:发起线程中断请求,只是请求并不会真的把线程给中断,实际上是把线程的中断标识设置为了true
-
interrupt的使用
Thread t = new Thread(){ @Override public void run() { while (true){ } } }; t.start(); Thread.sleep(1000); System.out.println(t.isInterrupted()); t.interrupt(); System.out.println(t.isInterrupted()); /*jvm不会退出线程还在死循环执行 终端输出输出结果是 false true */
- 一定要明白中断的含义,中断不是阻塞更不是让线程死掉。它只是告诉计算机出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。中断更多的是指线程标志位的某个状态,中断的处理逻辑需要用户自己来进行定制(你可以选择处理中断,也可以选择不处理),责任交给了用户
- 不是所状态的线程都能接受中断,比如new或者TERMINATED算发中断信号都无济于事。可以详细参考后面标题4 5 6
-
发送中断函数interrupt的jdk源码
//变量申明 private volatile Interruptible blocker; private final Object blockerLock = new Object(); //interrupt方法 public void interrupt() { if (this != Thread.currentThread()) checkAccess(); synchronized (blockerLock) { Interruptible b = blocker; if (b != null) { interrupt0(); // Just to set the interrupt flag b.interrupt(this); return; } } interrupt0(); } //interrupt0 本地方法来设置中断位的布尔类型 private native void interrupt0();
-
-
isInterrupted方法:判断线程的中断标识布尔类型, 不清除标志位.
public boolean isInterrupted() { return isInterrupted(false);//设置为false 不清除标志位 } //调用本地方法来判断中断位的布尔类型 并设置为false private native boolean isInterrupted(boolean ClearInterrupted);
-
interrupted方法:判断线程的中断标识是否为true还是false,并清除中断符号
//注意:是static的静态方法 public static boolean interrupted() { return currentThread().isInterrupted(true);//设置为true 清除标志位 } //调用本地方法来判断中断位的布尔类型 ClearInterrupted表示是否需要清除标志位 private native boolean isInterrupted(boolean ClearInterrupted);
##3 案列一:为什么interrupt触发后中断标志位为false
-
3.1 线程类
public class Worker implements Runnable{ @Override public void run() { System.out.println("Worker started."); try { System.out.println("Worker normal IsInterrupted: " + Thread.currentThread().isInterrupted()); Thread.sleep(5000); System.out.println("Worker normal IsInterrupted: " + Thread.currentThread().isInterrupted()); } catch (InterruptedException e) { System.out.println("Worker exception IsInterrupted: " + Thread.currentThread().isInterrupted()); } System.out.println("Worker stopped."); } }
-
3.2 主线程
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Worker());
t.start();
Thread.sleep(200);
t.interrupt();
System.out.println("Main thread stopped.");
}
-
3.3 输出
Worker started. Worker normal IsInterrupted: false Worker exception IsInterrupted: false Worker stopped. Main thread stopped.
-
3.4 详解
-
主线程在子线程睡眠的时候发起中断请求,线程中断仅仅是设置线程的中断状态位,不会停止线程。用户需要自己去监视线程的状态为并做处理。也就是说线程中断后会抛出InterruptedException的方法,比如这里的sleep,以及Object.wait等方法(就是在监视线程的中断状态),一旦Thread类中线程的中断状态被置为“中断状态”,就会抛出中断异常
-
由于在sleep的时候监控中断符号被设置true之后,线程会抛出中断异常InterruptedException。该异常一旦被抛出后,中断标志位将会被清除(wait join同理)
/** * @throws InterruptedException * if any thread has interrupted the current thread. The * <i>interrupted status</i> of the current thread is * cleared when this exception is thrown. */ public static native void sleep(long millis) throws InterruptedException;
-
注意:interrupt()方法发出的中断信号只能被wait() sleep() join()这三个方法捕捉到并产生中断
可以看到除了sleep之外,wait/join抛出异常都清除中断标志位
* @throws InterruptedException if any thread interrupted the * current thread before or while the current thread * was waiting for a notification. The <i>interrupted * status</i> of the current thread is cleared when * this exception is thrown. * @see java.lang.Object#notify() * @see java.lang.Object#notifyAll() */ public final native void wait(long timeout) throws InterruptedException;
* @throws InterruptedException * if any thread has interrupted the current thread. The * <i>interrupted status</i> of the current thread is * cleared when this exception is thrown. */ public final void join() throws InterruptedException { join(0); }
-
4 线程集中状态下对中断的反应
-
线程常见的几种状态
RUNNABLE:线程在运行或具备运行条件只是在等待操作系统调度
WAITING:线程在等待某个条件
TIMED_WAITING: 线程等待超时
BLOCKED: 线程在等待锁,试图进入同步块
NEW: 线程未启动
TERMINATED: 已结束
-
线程在这几种状态下对中断的反应和处理
-
RUNNABLE:中断只会设置线程的中断标志位,在当前的线程被外界触发中断后,可以在线程中使用wile不断监控线程的中断位是否变化从而控制线程的结束
public class InterruptRunnableDemo extends Thread { @Override public void run() { while (!Thread.currentThread().isInterrupted()) { // ... 单次循环代码 } System.out.println("done "); } public static void main(String[] args) throws InterruptedException { Thread thread = new InterruptRunnableDemo(); thread.start(); Thread.sleep(1000); thread.interrupt(); } }
-
Waiting: 这种线程状态只有在线程执行了方法join和wait会进入waiting状态。对线程对象调用interrupt()会使得该线程抛出InterruptedException,中断标志位会被清空(线程的中断标志位会由true重置为false
-
Timed_waiting:方法wait、sleep、join会让方法进入Timed_waiting状态。对线程对象调用interrupt()会使得该线程抛出InterruptedException,中断标志位会被清空(线程的中断标志位会由true重置为false
-
Blocked:interrupt()只是会设置线程的中断标志位,线程依然会处于BLOCKED状态,interrupt()并不能使一个在等待锁的线程真正”中断”。
-
new/terminate:调用interrupt()对它没有任何效果
-
5 IO操作对中断的反应
-
实现此InterruptibleChannel接口的通道是可中断:如果某个线程在可中断通道上因调用某个阻塞的 I/O 操作(serverSocketChannel. accept()、socketChannel.connect、socketChannel.open、socketChannel.read、socketChannel.write、fileChannel.read、fileChannel.write)而进入阻塞状态,而另一个线程又调用了该阻塞线程的 interrupt 方法,这将导致该通道被关闭,并且已阻塞线程接将会收到ClosedByInterruptException,并且设置已阻塞线程的中断状态。
-
如果线程阻塞于Selector调用,则线程的中断标志位会被设置,同时,阻塞的调用会立即返回
-
InputStream的Read不可中断,流中没有数据会阻塞,中断后会依然阻塞(是runnable状态 但不相应中断)
public class InterruptReadDemo { private static class A extends Thread { @Override public void run() { while(!Thread.currentThread().isInterrupted()){ try { System.out.println(System.in.read())//wait input } catch (IOException e) { e.printStackTrace(); } } System.out.println("exit"); } } public static void main(String[] args) throws InterruptedException { A t = new A(); t.start(); Thread.sleep(100); t.interrupt();//线程不会结束 一直等待IO输入 } }
##6 为什么线程可以在阻塞的时候捕获中断异常并处理(wait、sleep、join)
-
以下是三个方法的声明
public final native void wait(long var1) throws InterruptedException; public static native void sleep(long millis) throws InterruptedException; public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } }
-
阻塞和中断的区别
- 阻塞:表示线程的一种状态,线程是不占用CPU的(你的代码在执行过程中,在某个地方暂停了)
- 中断:计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。中断更多的是指线程标志位的某个状态,中断的处理逻辑需要用户自己来进行定制(你可以选择处理中断,也可以选择不处理),责任交给了用户。
-
哪些方法可以捕捉中断
一般说来,当可能阻塞的方法声明中有抛出 InterruptedException 则暗示该方法是可中断的,如 BlockingQueue#put、BlockingQueue#take、Object#wait、Thread#sleep 等。
##7 案列二:如何能使中断后的中断标志还是true
-
子线程
public class Worker1 implements Runnable{ @Override public void run() { System.out.println("Worker1 started."); try { System.out.println("Worker1 sleep started."); Thread.sleep(5000); System.out.println("Worker1 sleep end."); } catch (InterruptedException e) { System.out.println("Worker1 exception 1 IsInterrupted: " + Thread.currentThread().isInterrupted()); System.out.println("Worker1 exception 1 interrupted: " + Thread.currentThread().isInterrupted()); Thread.currentThread().interrupt(); // Thread.currentThread().interrupt(); //再次调用interrupt方法中断自己,将中断状态设置为“中断” System.out.println("Worker1 exception 2 IsInterrupted: " + Thread.currentThread().isInterrupted()); System.out.println("Worker1 exception 2 interrupted: " + Thread.interrupted()); System.out.println("Static Call: " + Thread.interrupted());//clear status } System.out.println("Worker stopped."); } }
-
主线程:不变
Thread t = new Thread(new Worker1()); t.start(); Thread.sleep(200); t.interrupt(); System.out.println("Main thread stopped.");
-
输出
Worker1 started. Worker1 sleep started. Main thread stopped. Worker1 exception 1 IsInterrupted: false Worker1 exception 1 interrupted: false Worker1 exception 2 IsInterrupted: true Worker1 exception 2 interrupted: true Static Call: false Worker stopped.
-
详解
- 由于抛出异常InterruptedException之后,中断位标志已经被清除成false,所以前两次打印是false(标志位为false是因为抛出异常 标志位被清除)
- 而再次调用线程的中断,可以将中断位置为true
##8 什么场景该使用要重置中断标志位
-
场景:sleep、wait、join三种方法被用在了一个没有申明抛出异常InterruptedException的方法里,但是又想告诉上层调用者这里发生了中断,就只能在catch里面进行重置。
-
线程代码
public class TaskRunner implements Runnable{ private BlockingQueue<Task> queue; public TaskRunner(BlockingQueue<Task> queue) { this.queue = queue; } @Override public void run() { try { while (true) { Task task = queue.take(); task.execute(); } } catch (InterruptedException e) { // Restore the interrupted status Thread.currentThread().interrupt(); } } }
-
LinkedBlockingQueue的take方法的源码
public E take() throws InterruptedException { E x; int c = -1; final AtomicInteger count = this.count; final ReentrantLock takeLock = this.takeLock; takeLock.lockInterruptibly(); try { while (count.get() == 0) { notEmpty.await(); } x = dequeue(); c = count.getAndDecrement(); if (c > 1) notEmpty.signal(); } finally { takeLock.unlock(); } if (c == capacity) signalNotFull(); return x; }
-
详解
- 当TaskRunner线程被中断被捕获并抛出异常InterruptedException
- catch抓住异常,该run方法没有将该异常抛出而是被捕获,倘若上层代码想通过中断标志位来显示线程是否被中断,就需要在这个地方再次中断使之为true,之后上层监控中断标志位来判断是否被中断