Thread的API详细介绍(二)线程的打断

1 线程打断(interrupt)

// 打断线程
public void interrupt();
// 是否打断
public static boolean interrupted();
// 是否打断
public boolean isInterrupted();

1.1 interrupt()方法

很多使线程阻塞的方法都会抛出InterruptedException异常,这个异常就是当线程进入阻塞的时候,调用了interrupt方法就会
抛出InterruptedException异常。这些方法有:

Object的wait方法。
Object的wait(long)方法。
Object的wait(long,int)方法。
Thread的sleep(long)方法。
Thread的sleep(long,int)方法。
Thread的join方法。
Thread的join(long)方法。
Thread的join(long,int)方法。
InterruptibleChannel的io操作。
Selector的wakeup方法。
。。。
  • 上述方法都会使得当前线程进入阻塞状态,若另外的一个线程调用被阻塞线程的interrupt方法,则会打断这种阻塞,抛出InterruptedException
  • 这个异常就像一个signal(信号)一样通知当前线程被打断了
  • 打断一个线程并不等于该线程的生命周期结束,仅仅是打断了当前线程的阻塞状态。

演示:

public static void main(String[] args) throws InterruptedException {

    // 创建一个线程
    Thread thread = new Thread(()->{
        try {
            // 线程休眠5s,输出hello world
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName() + "捕获到打断信号");
        }
        System.out.println("Hello World");
    },"MyThread");
    thread.start();
    // 主线程休眠2s后,调用MyThread的interrupt方法吗,打断MyThread
    TimeUnit.SECONDS.sleep(2);
    thread.interrupt();
}
MyThread捕获到打断信号
Hello World

会发现MyThread并没有休眠够5秒钟,就被主线程打断了,从休眠中醒来,继续执行了

interrupt这个方法到底做了什么样的事情呢?
- 在一个线程内部存在着名为interrupt flag的标识,如果一个线程被interrupt,那么它的flag将被设置true。
- 但是如果当前线程正在执行可中断方法被阻塞时,调用interrupt方法将其中断,反而会导致flag被清除。(!!!)
- 另外有一点需要注意的是,如果一个线程已经是死亡状态,那么尝试对其的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();
}

1.2 isInterrupted方法

isInterrupted是Thread的一个成员方法,它主要判断当前线程是否被中断,该方法仅仅是对interrupt标识的一个判断,并不会影响标识发生任何改变。

测试:在一个线程内部存在着名为interrupt flag的标识,如果一个线程被interrupt,那么它的flag将被设置true

public static void main(String[] args) throws InterruptedException {
     Thread thread = new Thread(() -> {
         while (true) {
             // do nothing, just empty loop
         }
     });
     
     thread.setDaemon(true);
     thread.start();
     
     TimeUnit.SECONDS.sleep(2);
     System.out.println("Thread is interrupted: " + thread.isInterrupted());
     thread.interrupt();
     System.out.println("Thread is interrupted: " + thread.isInterrupted());
 }

上面的代码中定义了一个线程,并且在线程的执行单元中(run方法)写了一个空的死循环,为什么不写sleep呢?因为sleep是可中断方法,会捕获到中断信号,从而干扰我们程序的结果。下面是程序运行的结果,记得手动结束上面的程序运行,或者你也可以将上面定义的线程指定为守护线程,这样就会随着主线程的结束导致JVM中没有非守护线程而自动退出。

运行结果:

Thread is interrupted: false
Thread is interrupted: true

可见当我们调用线程的interrupt方法之后,就会把线程的interrupt标识设置为true

测试:如果当前线程正在执行可中断方法被阻塞时,调用interrupt方法将其中断,反而会导致flag被清除

public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(() -> {
        while (true) {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                System.out.println("捕获到打断信号,打断标识被清除; Thread is interrupted: " + Thread.currentThread().isInterrupted());
            }
        }
    });

    thread.setDaemon(true);
    thread.start();

    TimeUnit.SECONDS.sleep(2);
    System.out.println("Thread is interrupted: " + thread.isInterrupted());
    thread.interrupt();
    System.out.println("Thread is interrupted: " + thread.isInterrupted());
}

输出:

Thread is interrupted: false
Thread is interrupted: true
捕获到打断信号,打断标识被清除; Thread is interrupted: false

可见当前线程正在执行可中断方法被阻塞时,调用interrupt方法将其中断,反而会导致flag被清除

其实这也不难理解,可中断方法捕获到了中断信号之后,为了不影响线程中其他方法的执行,将线程的interrupt标识复位是一种很合理的设计。

1.3 interrupted()方法

interrupted是一个静态方法,虽然其也用于判断当前线程是否被中断,但是它和成员方法isInterrupted还是有很大的区别的:

  • 调用该方法会直接擦除掉线程的interrupt标识。
  • 需要注意的是,如果当前线程被打断了,那么第一次调用interrupted方法会返回true,并且立即擦除了interrupt标识;第二次包括以后的调用永远都会返回false,除非在此期间线程又一次地被打断

验证一下上面的这句话:

public static void main(String[] args) throws InterruptedException {

     Thread thread = new Thread() {
         @Override
         public void run() {
             while (true) {
                 System.out.println(Thread.interrupted());
             }
         }
     };
     thread.setDaemon(true);
     thread.start();

     //shortly block make sure the thread is started.
     TimeUnit.MILLISECONDS.sleep(2);
     thread.interrupt();
 }

同样由于不想要受到可中断方法如sleep的影响,在Thread的run方法中没有进行任何短暂的休眠,所以运行上面的程序会出现非常多的输出,但是我们通过对输出的检查会发现如下所示的内容,其足以作为对该方法的解释。

false
false
false
false
true
false
false
false
false
false
false
false

在很多的false包围中发现了一个true,也就是interrupted方法判断到了其被中断,立即擦除了中断标识,并且只有这一次返回true,后面的都将会是false。

1.4 是否擦除线程interrupt的标识

翻看源码,其实内部都是调用的同一个本地方法,只是参数不同

public boolean isInterrupted() {
    return isInterrupted(false);
}
public static boolean interrupted() {
   return currentThread().isInterrupted(true);
}
// 其实内部都是调用的同一个本地方法,只是参数不同
private native boolean isInterrupted(boolean ClearInterrupted);

ClearInterrupted这个参数:主要用来控制是否擦除线程interrupt的标识

  • isInterrupted方法的源码中该参数为false,表示不想擦除
  • interrupted方法的源码中该参数为true,表示擦除

1.5 打断线程并不是终止线程

@Slf4j
public class InterruptTest1 {

    public static void main(String[] args) {
        // 定义一个线程
        Thread thread = new Thread("MyThread"){
            @Override
            public void run() {
                while (true){

                }
            }
        };
        thread.start();
        log.info("{} 是否已经打断: {}",thread.getName(),thread.isInterrupted());
        // 打断MyThread线程
        thread.interrupt();
        log.info("{} 是否已经打断: {}",thread.getName(),thread.isInterrupted());
        // 虽然这里MyThread线程被打断了,依然在运行
    }
}

1.6 提前打断

运行下面代码,观察结果。

public static void main(String[] args){

   System.out.println("Main thread is interrupted? " + Thread.interrupted());
   Thread.currentThread().interrupt();
   System.out.println("Main thread is interrupted? " + Thread.currentThread().isInterrupted());
   System.out.println(LocalDateTime.now());
   try{
       TimeUnit.MINUTES.sleep(5);
   } catch (InterruptedException e){
   		System.out.println(LocalDateTime.now());
       System.out.println("I will be interrupted still.");
   }
}
Main thread is interrupted? false
Main thread is interrupted? true
2021-03-29T15:12:28.468
2021-03-29T15:12:28.469
I will be interrupted still.

发现这两个时间的差根本不足5s,说明在前打断之后,执行到sleep之后,会立马捕获到这个打断标记,不再休眠

也就是说:如果一个线程设置了interrupt标识,那么接下来的可中断方法会立即中断

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值