一止长渊

中断

N 人看过
字数:1.1k字 | 预计阅读时长:4分钟

在 Core Java 中有这样一句话:”没有任何语言方面的需求要求一个被中断的程序应该终止。中断一个线程只是为了引起该线程的注意,被中断线程可以决定如何应对中断 “ 中断是一种协作机制。

也就是说其他线程调用目标线程的 interrupt 方法,并不会让目标线程停止和提前返回抛出 InterruptedException 表明自己提前返回了,调用 interrupt 方法只是给目标线程设置了一个标志位,友好地提示对方可不可以停止手中的事情提前返回,所以调用 interrupt 的方法产生结果完全是掌握在目标线程手中的,目标线程可以自己定义发现中断标志位 true 后的处理逻辑,是停下手中的事情然后还是不理会标志会继续做自己的事情。

1. 处于 Runnable 状态线程对中断标志位设置为了 true 后续的反应完全由自己决定

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new MyThread();
        t.start();
        Thread.sleep(1); // 暂停1毫秒
        t.interrupt(); // main线程中断目标t线程,只是向目标线程目标标志位interrupt设置为了true而已
        t.join(); // 等待t线程结束
        System.out.println("end");
    }
}

class MyThread extends Thread {
    public void run() {
        int n = 0;
        while (! isInterrupted()) { // 目标线程发现自己中断了,可以选择不理会继续执行任务,也可以选择停止手中的任务提前返回抛出InterruptedException
            n ++;
            System.out.println(n + " hello!");
        }
    }
}

class MyThread extends Thread {
    public void run() {
        int n = 0;
        while (true) { // 目标线程发现自己中断了,可以选择不理会继续执行任务,也可以选择停止手中的任务提前返回抛出InterruptedException
            n ++;
            System.out.println(n + " hello!");
        }
    }
}

2.处于 sleep 以及 wait 状态的线程对于中断标志位设置成了 true,中断发生会被唤醒,后续是否立刻相应中断以及何时响应中断由你自己决定(需要捕捉 InterruptedException)

实例代码

Thread t = new Thread (){
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        //exception被捕获,但是为输出为false 因为标志位会被清空
            System.out.println(isInterrupted());
        }
    }
};
t.start();
try {
    Thread.sleep(100);
} catch (InterruptedException e) {
}
t.interrupt();//置为true

这里典型可以参考 AQS 等待队列中的 Node,在独占模式下 Node 被唤醒存在两种原因:

  • 被中断唤醒
  • 头结点释放到头结点唤醒(该 Node 是等待队列中第一个状态正常的 Node,可能为 Head 的后继结点,后继结点取消就从后向前查找第一个)

当一个 Node 被其他线程调用中断 interpret 将状态标志位设置为 true,该 Node 唤醒后不会第一时间去处理中断,而是会有一个自旋检查前继结点以及状态,直到获取到资源后才会去响应中断,获取资源不成功是不会响应中断的,这期间可能又会进入到 waiting 状态。

    public final void acquire(int arg) {
        // 独占模式下,每个进入到AQS会首先去尝试获取资源,获取资源失败就会cas自旋入队
        // 此处体现了非公平锁,会出现等待队列中还有等待线程,但是一进来的线程去抢占加塞一次
        if (!tryAcquire(arg) &&   // 首先尝试获取资源,获取资源成功返回,获取资源失败,尝试Nodecas自旋入队
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // cas自旋入队成功后,进入等待休息状态
            selfInterrupt(); // 获取到资源后acquireQueued才会true,才会响应中断
    }

3. 处于 sleep 期间的线程对持有的锁不会进行释放

public class Debug26 {
    static ReentrantLock reentrantLock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        Thread t = new MyThread1();
        t.start();
        Thread.sleep(3000); // 主线程暂停3秒目的是为了让线程t抢占到锁
        System.out.println(reentrantLock.tryLock()); // 输出false
        t.join(); // 等待t线程结束
        System.out.println("end");
    }

    static class MyThread1 extends Thread {
        public void run() {
            reentrantLock.lock();
            try{
                Thread.currentThread().wait(10000);
            }catch(InterruptedException e){
                System.out.println("准备返回了...");
            }
        }
    }
}

本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 (CC BY-NC-ND 4.0) 进行许可。