引言

Java中线程的中断可能与我们想象的不太一样,在线程处于不同的状态时对该线程进行中断操作会有不同的结果。这篇文章,我们来看一下Java中线程的中断。

Thread中断API详解

  1. public void interrupt() {}
  2. public static boolean interrupted() {}
  3. public boolean isInterrupted() {}
  4. private native boolean isInterrupted(boolean ClearInterrupted);

第二个方法是static方法,其他三个都是实例方法。第四个方法是native方法,并且不能被外部访问,实际上,前三个方法都是通过第四个方法实现的。我们先来看这个方法:

  1. /**
  2. * Tests if some Thread has been interrupted. The interrupted state
  3. * is reset or not based on the value of ClearInterrupted that is
  4. * passed.
  5. */
  6. private native boolean isInterrupted(boolean ClearInterrupted);

根据注释,这个方法用来测试某些线程是否被中断。参数ClearInterrupted用来指定是否要对该线程的中断标志进行重置。如果是true,就表示要重置中断标志(重置就是将中断标志恢复为默认值,即没有被中断),否则就不重置。
我们再看实例方法isInterrupted():

  1. public boolean isInterrupted() {
  2. return isInterrupted(false);
  3. }

这个方法调用了上面的native方法并且参数总是false,也就是不重置中断状态。不重置也就对中断状态没有影响。
再看静态的interrupted()方法:

  1. public static boolean interrupted() {
  2. return currentThread().isInterrupted(true);
  3. }

这个方法是对当前线程调用native方法isInterrupted,但是传入的参数是true,也就是说,这个方法会重置当前线程的中断状态。如果我们连续调用这个方法两次,那么会返回false。
最后来看interrupt方法:

  1. public void interrupt() {
  2. if (this != Thread.currentThread())
  3. checkAccess();
  4. synchronized (blockerLock) {
  5. Interruptible b = blocker;
  6. if (b != null) {
  7. interrupt0(); // Just to set the interrupt flag
  8. b.interrupt(this);
  9. return;
  10. }
  11. }
  12. interrupt0();
  13. }

这同样是一个实例方法,也就是说,我们在中断某个线程时,需要指定中断哪个线程。中断并不意味着线程的执行会被中断,实际上,如果是被中断的线程正常运行中,那么对其调用中断操作只会设置它的中断状态为true,线程仍然会正常运行。
我们来看一个例子:

  1. public class Interrupt {
  2. public static void main(String[] args) throws InterruptedException {
  3. Thread thread = new Thread(new Runnable() {
  4. @Override
  5. public void run() {
  6. while(true){
  7. System.out.println("线程在运行");
  8. }
  9. }
  10. },"interrupted_thread");
  11. thread.start();
  12. thread.interrupt();
  13. }
  14. }

thread线程被main线程执行了中断操作,但是还会一直运行,一直会输出”线程在运行“。
上面只是线程在正常运行的情况,但是会有一些特殊情况,这些特殊情况包括:

  • 线程执行了Object.wait()、Object.wait(long)和Object.wait(long,int)方法。
  • 线程执行了Thread.join()、Thread.join(long)和Thread.join(long,int)方法。
  • 线程执行了sleep(long)、sleep(long、int)方法。

当线程已经执行了这些方法,再调用interrupt()方法对其进行中断操作时,会抛出InterruptedException方法,然后线程的中断状态会被清除。
看下面的例子:

  1. public class Interrupt {
  2. public static void main(String[] args) throws InterruptedException {
  3. Thread thread = new Thread(new Runnable() {
  4. @Override
  5. public void run() {
  6. try {
  7. Thread.sleep(100000);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. }
  12. },"interrupted_thread");
  13. thread.start();
  14. thread.interrupt();
  15. }
  16. }

thread线程调用了sleep方法处于TIMED-WAITING状态,然后main线程对其进行了中断操作,此时thread就会抛出InterruptedException异常,运行结果如下:

  1. java.lang.InterruptedException: sleep interrupted
  2. at java.lang.Thread.sleep(Native Method)
  3. at person.andy.concurrency.thread.Interrupt$1.run(Interrupt.java:9)
  4. at java.lang.Thread.run(Thread.java:748)

Thread.join()方法和Object.wait()方法也是同样的机制,下面是一个wait的示例:

  1. public class Interrupt {
  2. private static final Object lock = new Object();
  3. public static void main(String[] args) throws InterruptedException {
  4. Thread thread = new Thread(new Runnable() {
  5. @Override
  6. public void run() {
  7. synchronized (lock){
  8. try {
  9. lock.wait();
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. }
  15. },"interrupted_thread");
  16. thread.start();
  17. Thread.sleep(3000);
  18. thread.interrupt();
  19. }
  20. }

同样会抛出异常。像Thread.sleep(long)、Thread.join()和Object.wait()这类方法,是能响应中断的方法,我们看它们的声明,会发现它们都抛出了java.lang.InterruptedException异常。

正确使用中断

既然其他线程调用interrupt()并不能使线程真正的中断,那么我们能用中断来做什么呢?我们可以由被中断的线程自己不断地测试中断状态(调用isInterrupted()方法),如果被中断了,就自己停止做某些事情,或者说,如果没被中断,就一直做某些事情,看下面的例子:

  1. public class Interrupt {
  2. private static final Object lock = new Object();
  3. public static void main(String[] args) throws InterruptedException {
  4. Thread thread = new Thread(new Runnable() {
  5. @Override
  6. public void run() {
  7. while(!Thread.currentThread().isInterrupted()){
  8. System.out.println("interrupted_thread没有被中断");
  9. }
  10. System.out.println("interrupted_thread被中断了");
  11. }
  12. },"interrupted_thread");
  13. thread.start();
  14. Thread.sleep(1000);
  15. thread.interrupt();
  16. }
  17. }

interrupted_thread在while循环中不断地判断自己有没有被中断,如果没有,就继续运行,否则,跳出循环。
image.png
这么来看,中断实现了任务的取消,通常,中断是实现取消的最合理方式。