头图

大家好,我是半夏之沫 😁😁 一名金融科技领域的JAVA系统研发😊😊
我希望将自己工作和学习中的经验以最朴实最严谨的方式分享给大家,共同进步👉💓👈
👉👉👉👉👉👉👉👉💓写作不易,期待大家的关注和点赞💓👈👈👈👈👈👈👈👈
👉👉👉👉👉👉👉👉💓关注微信公众号【技术探界】 💓👈👈👈👈👈👈👈👈

前言

Java中的线程,有一个状态叫做 中断状态,用于标记线程是否被中断过,通过对线程中断状态的判断,可以实现例如 优雅终止线程唤醒线程 等功能。

Thread类中有interrupt()interrupted()和isInterrupted() 方法与线程的中断有关,本篇文章将对这些方法的具体作用进行详细解释。

在文章的最后,还会通过一个典型例子,演示如何通过线程的中断状态来优雅的终止线程。

正文

一. interrupt()方法详解

Thread类提供了interrupt() 方法来中断线程,哪个线程对象的interrupt() 方法被调用,那么这个线程就会被中断。关于interrupt() 方法,有如下注意点。

  1. interrupt() 方法是成员方法
  2. 通常不能在线程内部调用线程的 interrupt() 方法来中断线程自己
  3. 如果线程正阻塞在 Object#waitObject.wait(long)Object.wait(long, int)Thread#joinThread.join(long)Thread.join(long, int)Thread.sleep(long) Thread.sleep(long, int) 方法上,然后被 interrupt() 方法中断,此时中断状态会被重置为 falsefalse 表示未被中断),并且被中断的线程会收到中断异常 InterruptedException

上面最重要的就是第3点,第3点中罗列出来的方法在使用时都需要try-catch中断异常InterruptedException,但是在这个中断异常被抛出前,线程的中断状态会被重置为false,这就造成一种现象,因为被中断而从阻塞方法中唤醒的线程的中断状态是false

二. interrupted()方法详解

Thread类提供了静态方法interrupted() 来得到调用该方法的线程的中断状态。关于interrupted() 方法,有如下注意点。

  1. interrupted() 方法是静态方法
  2. 哪个线程调用 interrupted() 方法,就会返回这个线程的中断状态,然后线程的中断状态会重置为 false

interrupted() 方法在调用后,调用线程的中断状态会重置为false,这一点请切记。

三. isInterrupted()方法详解

Thread类提供了isInterrupted() 方法来得到线程的中断状态,哪个线程对象的isInterrupted() 方法被调用,就会返回这个线程的中断状态。关于isInterrupted() 方法,有如下注意点。

  1. isInterrupted() 方法是成员方法
  2. 哪个线程对象的 isInterrupted() 方法被调用,就会返回这个线程的中断状态,并且中断状态不会重置为 false

isInterrupted() 方法和interrupted() 方法都能获取到线程的中断状态,不同点在于isInterrupted() 方法获取到线程的中断状态后,线程的中断状态不会重置为false,而interrupted() 方法会。

四. 优雅的终止线程

首先明确一点,一个线程的终止,不能由其它线程来强行终止,这也就是为什么Thread#stopThread#suspend等方法会被废弃。

优雅终止线程的简单思路如下。

在线程执行的任务中不断的判断一个标志位,当标志位满足某种条件时,任务结束运行,从而线程优雅终止。

如下是一个简单示例。

public class InterruptedTest {

    @Test
    public void 优雅终止线程的简单示例() {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 5,
                60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

        // 执行任务
        for (int i = 0; i < 5; i++) {
            threadPool.execute(() -> {
                Thread thread = Thread.currentThread();
                while (!thread.isInterrupted()) {
                    // empty run
                }
                System.out.println(thread.getName() + " 任务执行完毕");
            });
        }

        // 主线程等待2秒,等待任务充分执行
        LockSupport.parkNanos(1000 * 1000 * 1000 * 2L);

        // shutdownNow线程池
        threadPool.shutdownNow();

        // 主线程等待1秒,等待任务关闭
        LockSupport.parkNanos(1000 * 1000 * 1000);
    }

}

上述示例中,每个线程执行的任务中会一直判断当前线程的中断状态,如果中断状态为true表示线程被中断了),那么就退出while循环,从而任务执行完毕,最终线程被优雅终止。

运行测试程序,打印如下。

pool-1-thread-5 任务执行完毕
pool-1-thread-4 任务执行完毕
pool-1-thread-3 任务执行完毕
pool-1-thread-2 任务执行完毕
pool-1-thread-1 任务执行完毕

运行结果表明线程确实被终止了。上述示例中线程执行的任务实际上是 响应中断 的,因此在线程被调用interrupt() 方法时,能够响应中断,从而结束任务的执行,最终线程终止。

再看如下一个例子,是一个看起来响应中断,却实际不响应中断的典型错误案例。

public class InterruptedTest {

    @Test
    public void 线程池中的任务不响应中断的典型案例() {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 5,
                60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

        // 执行任务
        for (int i = 0; i < 5; i++) {
            threadPool.execute(() -> {
                Thread thread = Thread.currentThread();
                while (!thread.isInterrupted()) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        System.out.println(thread.getName() + " 被中断了");
                    }
                }
                System.out.println(thread.getName() + " 任务执行完毕");
            });
        }

        // 主线程等待2秒,等待任务充分执行
        LockSupport.parkNanos(1000 * 1000 * 1000 * 2L);

        // shutdownNow线程池
        threadPool.shutdownNow();

        // 主线程等待1秒,等待任务关闭
        LockSupport.parkNanos(1000 * 1000 * 1000);
    }

}

上述示例中,任务大部分时间是阻塞在Thread.sleep(long) 方法上的,但其实Thread.sleep(long) 方法是响应中断的,所以上述示例是希望线程被中断时,线程从Thread.sleep(long) 方法返回,然后判断线程的中断状态,然后结束任务的运行,最终线程终止。

运行测试程序,打印结果如下。

pool-1-thread-1 被中断了
pool-1-thread-3 被中断了
pool-1-thread-4 被中断了
pool-1-thread-2 被中断了
pool-1-thread-5 被中断了

可见任务没有结束运行,故线程也并没有被终止。原因就是第一节中提到的,线程阻塞在Thread.sleep(long) 方法上时如果被中断,那么中断状态会被重置为false然后抛出中断异常InterruptedException,那么上述示例中,任务在判断线程的中断状态时,中断状态会恒为false,故任务永远不会退出。

改进代码如下所示。

public class InterruptedTest {

    @Test
    public void 线程池中的任务不响应中断的典型案例改进() {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 5,
                60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

        // 执行任务
        for (int i = 0; i < 5; i++) {
            threadPool.execute(() -> {
                Thread thread = Thread.currentThread();
                while (!thread.isInterrupted()) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        // 重新将中断状态置为true
                        thread.interrupt();
                        System.out.println(thread.getName() + " 被中断了");
                    }
                }
                System.out.println(thread.getName() + " 任务执行完毕");
            });
        }

        // 主线程等待2秒,等待任务充分执行
        LockSupport.parkNanos(1000 * 1000 * 1000 * 2L);

        // shutdownNow线程池
        threadPool.shutdownNow();

        // 主线程等待1秒,等待任务关闭
        LockSupport.parkNanos(1000 * 1000 * 1000);
    }

}

改进思路就是在任务中捕获到InterruptedException后,再调用一次当前线程对象的interrupt() 方法,来将当前线程的中断状态重新置为true,从而任务可以顺利结束运行,最终线程终止。

运行测试程序,打印结果如下。

pool-1-thread-5 被中断了
pool-1-thread-5 任务执行完毕
pool-1-thread-1 被中断了
pool-1-thread-1 任务执行完毕
pool-1-thread-2 被中断了
pool-1-thread-2 任务执行完毕
pool-1-thread-3 被中断了
pool-1-thread-4 被中断了
pool-1-thread-4 任务执行完毕
pool-1-thread-3 任务执行完毕

可见任务是顺利执行完毕,从而线程完成了优雅终止。

总结

关于Java中线程的 中断 总结如下。

  1. Thread 类提供了 interrupt() 成员方法来中断线程。哪个线程对象的interrupt() 方法被调用,那么这个线程被中断,中断状态会被置为true。但是如果线程是阻塞在Object#waitThread#sleep等方法上时被调用interrupt() 方法来中断,那么中断状态会被重置为false,然后抛出中断异常InterruptedException
  2. Thread 类提供了 interrupted() 静态方法来获取线程的中断状态。哪个线程调用interrupted() 方法,就返回这个线程的中断状态,同时也 重置这个线程的中断状态为false
  3. Thread 类提供了 isInterrupted() 成员方法来获取线程的中断状态。哪个线程对象的isInterrupted() 方法被调用,那么就返回这个线程的中断状态,并且 不会 重置这个线程的中断状态为false

线程的 优雅终止 思路如下。

在线程执行的任务中不断的判断一个标志位,当标志位满足某种条件时,任务结束运行,从而线程优雅终止。

大家好,我是半夏之沫 😁😁 一名金融科技领域的JAVA系统研发😊😊
我希望将自己工作和学习中的经验以最朴实最严谨的方式分享给大家,共同进步👉💓👈
👉👉👉👉👉👉👉👉💓写作不易,期待大家的关注和点赞💓👈👈👈👈👈👈👈👈
👉👉👉👉👉👉👉👉💓关注微信公众号【技术探界】 💓👈👈👈👈👈👈👈👈


半夏之沫
76 声望34 粉丝