Java-基础-interrupt()

Java-基础-interrupt()

1. InterruptedException

  • 当一个方法后面声明可能会抛出InterruptedException异常时,说明该方法是可能会花一点时间,但是可以取消的方法。
  • InterruptedException的代表方法有:
    • java.lang.Object 类的 wait 方法
    • java.lang.Thread 类的 sleep 方法
    • java.lang.Thread 类的 join 方法

– 需要花点时间的方法

  • 执行wait方法的线程,会进入等待区等待被notify/notify All。在等待期间,线程不会活动。

  • 执行sleep方法的线程,会暂停执行参数内所设置的时间。

  • 执行join方法的线程,会等待到指定的线程结束为止。

因此,上面的方法都是需要花点时间的方法。

– 可以取消的方法

  • 因为需要花时间的操作会降低程序的响应性,所以可能会取消/中途放弃执行这个方法。
  • 这里主要是通过interrupt方法来取消。
  1. sleep方法与interrupt方法
  • interrupt方法是Thread类的实例方法,在执行的时候并不需要获取Thread实例的锁定,任何线程在任何时刻,都可以通过线程实例来调用其他线程的interrupt方法。
  • 当在sleep中的线程被调用interrupt方法时,就会放弃暂停的状态,并抛出InterruptedException异常,这样一来,线程的控制权就交给了捕捉这个异常的catch块了。
  1. wait方法和interrupt方法
  • 当线程调用wait方法后,线程在进入等待区时,会把锁定解除
  • 当对wait中的线程调用interrupt方法时,会先重新获取锁定,再抛出InterruptedException异常,获取锁定之前,无法抛出InterruptedException异常。
  1. join方法和interrupt方法
  • 当线程以join方法等待其他线程结束时,一样可以使用interrupt方法取消。因为join方法不需要获取锁定,故而与sleep一样,会马上跳到catch程序块

线程中断

  • 每一个线程都有一个boolean类型的标志,此标志意思是当前的请求是否请求中断,默认为false。
  • 当一个线程A调用了线程B的interrupt方法时,那么线程B的是否请求的中断标志变为true。而线程B可以调用方法检测到此标志的变化。
  1. 对于阻塞方法(join,sleep,wait)
  • 如果线程B调用了阻塞方法,如果是否请求中断标志变为了true,那么它会抛出InterruptedException异常。抛出异常的同时它会将线程B的是否请求中断标志置为false
  1. 对于非阻塞方法
  • 可以通过线程B的isInterrupted方法进行检测是否请求中断标志为true还是false
  • 另外还有一个静态的方法interrupted方法也可以检测标志。但是静态方法它检测完以后会自动的将是否请求中断标志位置为false

讲下列三个方法的时候先给出总结:

  • 调用interrupt()方法仅仅是在当前线程中打了一个停止的标记,并不是真正的停止线程
  • interrupted()测试当前线程是否已经是中断状态,执行后具有清除中断状态flag的功能
  • isInterrupted()测试线程Thread对象是否已经是中断状态,但不清除中断状态flag

2. interrupt()方法

  • interrupt方法其实只是改变了中断状态而已。
  • 而sleep、wait和join这些方法的内部会不断的检查中断状态的值,从而自己抛出InterruptEdException。
1
所以,如果在线程进行其他处理时,调用了它的interrupt方法,线程也不会抛出InterruptedException的,只有当线程走到了sleep, wait, join这些方法的时候,才会抛出InterruptedException。若是没有调用sleep, wait, join这些方法,或者没有在线程里自己检查中断状态,自己抛出InterruptedException,那InterruptedException是不会抛出来的。

mark

3. isInterrupted() 方法

  • 可以用来检查中断状态,并且不会清除
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
public class InterruptTest3 {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " i = " + i);
}
});

thread.start();

// main thread interrupt
Thread.currentThread().interrupt();

System.out.println(thread.getName() + ":" + thread.isInterrupted());
System.out.println(thread.getName() + ":" + thread.isInterrupted());
System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().isInterrupted());
System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().isInterrupted());

// thread interrupt
thread.interrupt();

System.out.println(thread.getName() + ":" + thread.isInterrupted());
System.out.println(thread.getName() + ":" + thread.isInterrupted());
}
}

mark

4. interrupted() 方法

  • 可以用来检查并清除中断状态。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class InterruptTest2 {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " i = " + i);

if (i == 5) {
Thread.currentThread().interrupt();

System.out.println("interrupted 1: " + Thread.interrupted());

System.out.println("interrupted 2: " + Thread.interrupted());
}
}
});

thread.start();

}
}

mark

  • 控制台第一次打印的结果是true,第二次为false;
  • Java Doc中给出的解释是:测试当前线程是否已经中断,线程的中断状态由该方法清除。即如果连续两次调用该方法,则第二次调用将返回false(在第一次调用已清除flag后以及第二次调用检查中断状态之前,当前线程再次中断的情况除外)
  • 所以,interrupted()方法具有清除状态flag的功能

5. InterruptedException 的处理

  • 现在知道线程可以检测到自身的标志位的变化,但是他只是一个标志,如果线程本身不处理的话,那么程序还是会执行下去,就好比,老师在学校叮嘱要好好学习,具体什么时候,如何好好学习还是看自身。
  • 因此interrupt() 方法并不能立即中断线程,该方法仅仅告诉线程外部已经有中断请求,至于是否中断还取决于线程自己

简单的了解了什么是阻塞和中断以后,我们就该了解碰到InterruptedException异常该如何处理了。

一句话:不要不管不顾

  • 有时候阻塞的方法抛出InterruptedException异常并不合适,例如在Runnable中调用了可中断的方法,因为你的程序是实现了Runnable接口,然后在重写Runnable接口的run方法的时候,那么子类抛出的异常要小于等于父类的异常。而在Runnablerun方法是没有抛异常的。所以此时是不能抛出InterruptedException异常
  • 如果此时你只是记录日志的话,那么就是一个不负责任的做法因为在捕获InterruptedException异常的时候自动的将 是否请求中断标志置为了false。
  • 至少在捕获了InterruptedException异常之后,如果你什么也不想做,那么就将标志重新置为true以便栈中更高层的代码能知道中断,并且对中断作出响应。

捕获到InterruptedException异常后恢复中断状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class TaskRunner implements Runnable {
private BlockingQueue<Task> queue;

public TaskRunner(BlockingQueue<Task> queue) {
this.queue = queue;
}

public void run() {
try {
while (true) {
Task task = queue.take(10, TimeUnit.SECONDS);
task.execute();
}
}
catch (InterruptedException e) {
// Restore the interrupted status
// 向上保存中断的状态
Thread.currentThread().interrupt();
}
}
}
打赏
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2019-2022 Zhuuu
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信