专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Thread.interrupt()实战

问题背景

  今天查看项目的代码的时候,发现同事写了一段代码,想通过Thread.interrupt来退出正在执行的线程,代码大概如下:

public class ThreadInterruptTest {

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

        Thread t1 = new Thread(() -> {
            while (true) {
                try {
                    // do something
                    // do something
                    // do something

                    Thread.sleep(3 * 1000); //A处

                    /* do something
                       do something
                       do something */
                } catch (InterruptedException e) { //C处
                    Thread.currentThread().interrupt //D处
                    break;
                }
            }
        });

        //运行线程
        t1.start();

        Thread.sleep(10 * 1000);

        //打断线程
        t1.interrupt(); //B处
    }

}

  这段代码希望通过B处执行的t1.interrupt来打断线程t1的执行。
  仔细看会发现这段代码是有问题的。我们先来看下Thread.interrupt方法的源码以及它的注释(每一段英文注释我都简单的翻译了下,如有不正确的地方,劳烦各位指出):

 /**
     * Interrupts this thread.
     * 打断当前这个线程
     *
     * <p> Unless the current thread is interrupting itself, which is
     * always permitted, the {@link #checkAccess() checkAccess} method
     * of this thread is invoked, which may cause a {@link
     * SecurityException} to be thrown.
     * 无论什么时候当前线程内interrupt自己都是被允许的。但是如果当前线程interrupt的
     * 不是当前线程,则会调用checkAccess方法,该方法内通过SecurityManager来检查这个
     * 操作是否能被允许。
     *
     * <p> If this thread is blocked in an invocation of the {@link
     * Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link
     * Object#wait(long, int) wait(long, int)} methods of the {@link Object}
     * class, or of the {@link #join()}, {@link #join(long)}, {@link
     * #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
     * methods of this class, then its interrupt status will be cleared and it
     * will receive an {@link InterruptedException}.
     * 如果被打断的线程正因为以下情况而被阻塞:Object.wait,Thread.join,Thread.sleep,
     * 调用interrupt方法后会将被打断的线程的interrupt标志位重置,并抛出InterruptedException
     *
     * <p> If this thread is blocked in an I/O operation upon an {@link
     * java.nio.channels.InterruptibleChannel InterruptibleChannel}
     * then the channel will be closed, the thread's interrupt
     * status will be set, and the thread will receive a {@link
     * java.nio.channels.ClosedByInterruptException}.
     * 如果被打断的线程正被阻塞在一个InterruptibleChannel对象的I/O操作上,调用
     * interrupt方法同样也会将被打断的线程的interrupt标志位重置,并抛出ClosedByInterruptException
     * 
     * <p> If this thread is blocked in a {@link java.nio.channels.Selector}
     * then the thread's interrupt status will be set and it will return
     * immediately from the selection operation, possibly with a non-zero
     * value, just as if the selector's {@link
     * java.nio.channels.Selector#wakeup wakeup} method were invoked.
     * 如果被打断的线程正被Selector阻塞,则调用interrupt后,当前线程的interrupt标志位会被设置,
     * 且将会马上从这个selection操作上返回,返回值大概率为一个非0的值。上述调用interrupt方法的效果,和
     * 调用Selector的wakeup方法是等效的。
     *
     * <p> If none of the previous conditions hold then this thread's interrupt
     * status will be set. </p>
     * 如果在调用当前线程的interrupt方法的时候,不属于上述任意一种情况,则interrupt操作
     * 只会去设置当前线程的interrupt标志位,将其设置为true。
     *
     * <p> Interrupting a thread that is not alive need not have any effect.
     * interrupt一个非alive的线程将不会有任何的效果。
     *
     * @throws  SecurityException
     *          if the current thread cannot modify this thread
     *
     * @revised 6.0
     * @spec JSR-51
     */
    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();
    }

  结合上述Thread.interrupt方法的注释以及我同事写的代码。一共有下述两个错误点:

1、 当执行B处的代码时,线程t1的while循环中并不一定正在执行Thread.sleep,所以不一定会抛出InterruptedException。
2、 如果执行B处的代码时,线程t1恰好在sleep(执行A处的代码),会抛出InterruptedException,为了能退出当前线程,其实只要写个break就行了,没必要加上D处的代码,再次interrupt一下当前的线程(应该是没理解interrupt方法的作用,以为调用interrupt方法就会打断当前正在运行的线程并将它退出)。

改进同事的代码

public class ThreadInterruptTest {

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

        Thread t1 = new Thread(() -> {
            while (!Thread.interrupted()) {
                // do something
                // do something
                // do something

               // Thread.sleep(3 * 1000);

                // do something
                // do something
                // do something
            }
        });

        //运行线程
        t1.start();

        Thread.sleep(10 * 1000);

        //打断线程
        t1.interrupt();
    }

}

Thread.interrupt小结

   Thread.interrupt方法的逻辑是不会打断线程的,它只是会将当前线程的interrupt标志位设置为true。如果你的代码逻辑中要要响应Thread.interrupt方法,则只需要判断当前线程的interrupt标志位的值就可以了,可以通过Thread的isInterrupted和interrupted方法(两个方法的区别是前者调用后不会重置线程的interrupt标志位,而后者会)来获取当前线程的interrupt状态,根据interrupt的状态来执行自己期望的逻辑。没有看过Thread.sleep(native方法)的源码,但是我猜应该也是按照上述方式来实现对Thread.interrupt方法的响应。

文章永久链接:https://tech.souyunku.com/26337

未经允许不得转载:搜云库技术团队 » Thread.interrupt()实战

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们