Java 线程中断机制,interrupt 源码解析 - Thread 中断系列全搞定

前言

Java并发知识体系持续更新:https://blog.csdn.net/m0_46144826/category_9881831.html

开始之前老规矩,把源码放上:

  • Thread 源码解读注释:https://github.com/qianwei4712/JDK1.8.0.25-read/blob/master/src/main/java/java/lang/Thread.java
  • Object 源码解读注释:https://github.com/qianwei4712/JDK1.8.0.25-read/blob/master/src/main/java/java/lang/Object.java

一个线程执行完毕之后会自动结束,如果在运行过程中发生异常也会提前结束。

线程中断是一个非常重要的概念,中断线程是取消任务最好的方法。

在 Java 的 Thread 类中,提供了以下三个关于线程中断的 public 方法:

  • void interrupt() ,中断线程。
  • static boolean interrupted() ,测试当前线程是否已被中断。通过此方法可以清除线程的中断状态。
  • boolean isInterrupted() ,测试线程是否已经中断,线程的中断状态不受该方法的影响。

这三个是线程的公共方法,但是它的最底层是两个 native 方法:

    private native void interrupt0(); //中断线程
    /**
     * 测试某些线程是否已被中断。线程的中断状态不受此方法的影响。
     * ClearInterrupted参数决定线程中断状态是否被重置,若为true则重置。
     */
    private native boolean isInterrupted(boolean ClearInterrupted);

从这里就可以发现:

线程的中断状态,并不是由 Java 来决定。实际上,Thread 类中并没有维护线程的中断状态。


协作式和抢占式

然后,就有了下一个问题:

中断状态是个啥玩意儿???线程中断难道不是终止线程嘛?为什么还会有状态?Thread 类里面也没有这个状态变量啊,难道操作系统内给线程添加了这个状态?

所以,又又又又得百度去咯。。。根据各位大佬们的博客,个人感觉应该是这样的:

  1. 明确线程中断状态不是 Thread 类的标志位,而是操作系统中对线程的中断标志。

这一结论来自 native 方法:

    /**
     * 测试某些线程是否已被中断。线程的中断状态不受此方法的影响。
     * ClearInterrupted参数决定线程中断状态是否被重置,若为true则重置。
     */
    private native boolean isInterrupted(boolean ClearInterrupted);

很明显了,在操作系统对线程的管理中,确实存在一个中断状态的标志位。


  1. 操作系统调度线程的方式:协作式、抢占式
  • 协作式:线程自己的工作执行完后,要主动通知调度切换到另一个线程上。 如果一个线程编写有问题,一直不告知系统进行线程切换,那么程序就会一直阻塞在那里。线程的执行时间由自身掌控。
  • 抢占式:线程将由调度来分配执行时间,线程的切换不由线程自身决定。 不会出现一个线程导致整个进程阻塞的问题。

Java 的线程中断,也作为协作式中断,调用 interrupt() 方法最终也只是更改操作系统中的中断标志位,线程是否中断,由线程自身决定。


isInterrupted()

测试线程是否已经中断,线程的中断状态不受该方法的影响。

    /**
     * 测试此线程是否已被中断。线程的中断状态不受此方法的影响。
     * 

如果中断时,线程并没有存活,那么该方法返回 false。意思就是,如果线程还没有 start 启动,或者已经消亡,那么返回依然是 false. * @return 如果该线程已被中断,返回true;否则返回 false */ public boolean isInterrupted() { //不清除中断状态 return isInterrupted(false); }

这是最基础的判断中断状态,且不更改 OS 线程中断标志位。可以对照上面的 native 方法阅读。特别的记一下这两句:

如果线程还没有 start 启动,或者已经消亡,那么返回依然是 false。中断状态只代表是否有线程调用中断方法。

举个栗子看下效果就行:

   public static class Example extends Thread{
        @Override
        public void run() {
            while (!isInterrupted()){
            }
            System.out.println("线程中断");
        }
    }

然后 main 启动:

    public static void main(String[] args) throws Exception {
        Example example = new Example();
        System.out.println("当前线程 example 尚未启动,此时状态:" + example.isInterrupted());
        example.start();
        System.out.println("当前线程 example 启动且尚未调用 interrupt,此时状态:" + example.isInterrupted());
        example.interrupt();
        System.out.println("当前线程 example 启动且调用 interrupt,此时状态:" + example.isInterrupted());
        Thread.sleep(5000);
        System.out.println("等待 5 秒,example 应该已经消亡,此时状态:" + example.isInterrupted());
    }

输出状态为:

当前线程 example 尚未启动,此时状态:false
当前线程 example 启动且尚未调用 interrupt,此时状态:false
当前线程 example 启动且调用 interrupt,此时状态:true
线程中断
等待 5 秒,example 应该已经消亡,此时状态:false

interrupt()

中断线程

先看下源码,以及官方(我自个的渣渣翻译)注释:

    /**
     * 中断此线程。
     * 

线程可以中断自身,这是允许的。在这种情况下,不用进行安全性验证({@link #checkAccess() checkAccess} 方法检测) *

若当前线程由于 wait() 方法阻塞,或者由于join()、sleep()方法,然后线程的中断状态将被清除,并且将收到 {@link InterruptedException}。 *

如果线程由于 IO操作({@link java.nio.channels.InterruptibleChannel InterruptibleChannel})阻塞,那么通道 channel 将会关闭, * 并且线程的中断状态将被设置,线程将收到一个 {@link java.nio.channels.ClosedByInterruptException} 异常。 *

如果线程由于在 {@link java.nio.channels.Selector} 中而阻塞,那么线程的中断状态将会被设置,它将立即从选择操作中返回。 *该值可能是一个非零值,就像调用选择器的{@link java.nio.channels.Selector#wakeupakeup}方法一样。 * *

如果上述条件均不成立,则将设置该线程的中断状态。

*

中断未运行的线程不必产生任何作用。 * @throws SecurityException 如果当前线程无法修改此线程 */ public void interrupt() { //如果调用中断的是线程自身,则不需要进行安全性判断 if (this != Thread.currentThread()) checkAccess(); synchronized (blockerLock) { Interruptible b = blocker; if (b != null) { interrupt0(); // 只是设置中断标志 b.interrupt(this); return; } } interrupt0(); }

这个方法的作用,在上面的栗子中已经有过体现了,接着来测试下源码中提到的异常:

    public static void main(String[] args) throws Exception {
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(2000);
                System.out.println("线程 sleep 结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.start();
        thread.interrupt();
        System.out.println("main 线程调用 interrupt 结束");
        System.out.println("线程抛出了中断异常,此时状态:" + thread.isInterrupted());
        Thread.sleep(5000);
        System.out.println("等待 5 秒,线程应该已经消亡,此时状态:" + thread.isInterrupted());
    }

运行结果:

main 线程调用 interrupt 结束
线程抛出了中断异常,此时状态:true
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at thread.InterruptTest.lambda$test2$0(InterruptTest.java:20)
	at thread.InterruptTest$$Lambda$1/791452441.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:745)
等待 5 秒,线程应该已经消亡,此时状态:false

在 sleep 过程中调用中断,抛出中断异常。。线程不会往下执行。

虽然抛出了异常,但是线程的中断状态确实设置成功了。。。只是线程被立刻从 sleep 中唤醒。

嗯,这两这个好理解。。。。。

Java 线程中断机制,interrupt 源码解析 - Thread 中断系列全搞定_第1张图片


interrupted()

测试当前线程是否已被中断。通过此方法可以清除线程的中断状态。

    /**
     * 测试当前线程是否已被中断。
     * 通过此方法可以清除线程的中断状态.
     * 换句话说,如果此方法要连续调用两次,则第二个调用将返回false(除非当前线程在第一个调用清除了它的中断状态之后,且在第二个调用对其进行检查之前再次中断)
     * 

如果中断时,线程并没有存活,那么该方法返回 false * @return 如果该线程已被中断,返回true;否则返回 false */ public static boolean interrupted() { //清除线程的中断状态 return currentThread().isInterrupted(true); }

说实话,这个方法的作用,实在是有点迷。。。。

public static class Example2 extends Thread{
        @Override
        public void run() {
            System.out.println("调用 interrupt 方法,线程中断");
            interrupt();
            System.out.println("尚未调用 interrupted 方法,此时线程中断状态:" + isInterrupted());
            System.out.println("线程第 1 次调用 interrupted 方法,方法返回:" + Thread.interrupted());
            System.out.println("线程第 1 次调用 interrupted 方法结束后,此时线程中断状态:" + isInterrupted());
            System.out.println("线程第 2 次调用 interrupted 方法,方法返回:" + Thread.interrupted());
            System.out.println("线程第 2 次调用 interrupted 方法结束后,此时线程中断状态:" + isInterrupted());
            System.out.println("线程第 3 次调用 interrupted 方法,方法返回:" + Thread.interrupted());
            System.out.println("线程第 3 次调用 interrupted 方法结束后,此时线程中断状态:" + isInterrupted());
        }
    }

mian 方法测试:

    public static void main(String[] args) throws Exception {
        Example2 example = new Example2();
        example.start();
    }

控制台输出结果为:

调用 interrupt 方法,线程中断
尚未调用 interrupted 方法,此时线程中断状态:true
线程第 1 次调用 interrupted 方法,方法返回:true
线程第 1 次调用 interrupted 方法结束后,此时线程中断状态:false
线程第 2 次调用 interrupted 方法,方法返回:false
线程第 2 次调用 interrupted 方法结束后,此时线程中断状态:false
线程第 3 次调用 interrupted 方法,方法返回:false
线程第 3 次调用 interrupted 方法结束后,此时线程中断状态:false

乍一看,感觉需要挠头。。。这个方法的作用也忒不明显了。。。 java doc 也不讲的不清楚。

Java 线程中断机制,interrupt 源码解析 - Thread 中断系列全搞定_第2张图片

仔细一想,是这样的:

  • 作为一个 static 方法,作用于当前线程
  • 调用 static boolean interrupted() 方法后,先将线程的中断状态设置为 false,再返回原先的中断状态

参考文章

https://blog.csdn.net/oldshaui/article/details/106952102

https://blog.csdn.net/qq_41901915/article/details/103654263

https://www.cnblogs.com/L-a-u-r-a/p/8575217.html

https://blog.csdn.net/tianjindong0804/article/details/105134182

你可能感兴趣的:(#,并发知识体系)