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

基础复盘 - 线程中断

前言

  • 2月份买的airpods pro右耳开始出现:稍微晃动就会有哒哒的声音,去售后检测,说是硬件问题,也有客户反馈,如果大家有这问题的,保内可以去免费换。。。

正题开始

在编程中,肯定对InterruptException很熟悉,举个经常有的例子就是Thread.sleep(1000)。那么,这个InterruptException到底源于什么呢?什么时候会抛出呢?

中断与Thread.sleep(long)等方法的关系

  • 源自 Thread.interrupt()方法注释(还有其他方法与中断有关系,详情看注释)。
* <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}.

  • 重点在interrupt status will be cleared and it will receive an InterruptedException

中断标识位将被清除,然后抛出InterruptedException

  • 关于中断标识位清除,后续代码说明。

Thread.interrupt()

  • 可以看下源码怎么说,Thread.interrupt(),笔者比较懒,加上注释也很直白。

// Just to set the interrupt flag

  • 可见,中断可以理解为线程的标识位属性,线程可以通过标识符判断是否被中断,根据判断结果进行响应。

Thread.interrupted()

  • 判断当前线程是否被中断的方法有2种:

1、Thread.interrupted() 2、Thread.currentThread().isInterrupted()

  • 这2种方法,最后都会调用一个native方法,只是说ClearInterrupted参数会不一样。
// Thread.interrupted() ClearInterrupted = true
public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }
// Thread.currentThread().isInterrupted() ClearInterrupted = false
public boolean isInterrupted() {
        return isInterrupted(false);
    }
 /**
     * Tests if some Thread has been interrupted.  The interrupted state
     * is reset or not based on the value of ClearInterrupted that is
     * passed.
     */
    private native boolean isInterrupted(boolean ClearInterrupted);

  • 那么为什么要有ClearInterrupted的差异呢?可以看下代码。

结合代码说明ClearInterrupted的作用(实际不能这么写 只是为了说明)

  • Thread.interrupt()重置中断标识位,为什么要重置标识位呢?例如,对一个线程一先一后调用了2次Thread.interrupt()时,可以知道第2次调用后的状态
  • 测试代码:
public class InterruptTest {
    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(new InterruptRunnable());
        thread.start();
        // 第一次中断
        thread.interrupt();
        Thread.sleep(1000);
        // 第二次中断
        thread.interrupt();
    }

    private static class InterruptRunnable implements Runnable {

        AtomicInteger interruptCount = new AtomicInteger(0);

        @Override
        public void run() {
           while (true) {
               if (Thread.interrupted()) {
                   System.out.println("被中断了" + interruptCount.incrementAndGet() + "次");
               }
               // 此处是无限循环 。。。
           }

        }
    }
}

  • 结果:
被中断了1次
被中断了2次
...无限循环 但是没继续输出

  • 那么,不重置呢?很显然,第一次调用就把标识位置成true了,第2次调用形同虚设。。。显然就是无限循环。
   private static class InterruptRunnable implements Runnable {

        AtomicInteger interruptCount = new AtomicInteger(0);

        @Override
        public void run() {
           while (true) {
               if (Thread.interrupted()) {
                   System.out.println("被中断了" + interruptCount.incrementAndGet() + "次");
               }
               // 此处是无限循环 。。。
           }

        }
    }
}

  • 真·无限循环
被中断了880090次
被中断了880091次
被中断了880092次
被中断了880093次
被中断了880094次
被中断了880095次
... 手动暂停

与sleep结合下

那么,sleep抛出InterruptionException清除标识位又是什么含义呢?

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

        Thread thread = new Thread(new SleepRunnable());
        thread.start();
        Thread.sleep(1000);
        // 第一次中断
        thread.interrupt();

    }

    private static class SleepRunnable implements Runnable {

        @Override
        public void run() {
            int i = 0;
            // 如果被中断跑出循环
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    Thread.sleep(500);
                    System.out.println(i++);
                } catch (InterruptedException e) {
                    // sleep清除标识位
                    System.out.println("当前是否被中断?答案是:" + Thread.currentThread().isInterrupted());
                }
            }
            System.out.println("跑出循环??");
        }
    }

  • 没跑出循环
0
当前是否被中断?答案是:false
1
2
3
4
5
6
7
... 无限循环

结束线程的方式

  • 经过上述分析,基本理解了中断的基本概念,以及容易忽略的知识点,那么中断的必要性在哪儿呢?关于结束线程

过期的stopsuspendresume

这里,以stop为例,我们使用代码看看为什么不能使用上述方法。

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

        Thread thread = new Thread(new StopRunnable());
        thread.start();
        Thread.sleep(500);
        thread.stop();

        System.out.println(getInfo());

    }

    private static class StopRunnable implements Runnable {

        @Override
        public void run() {
            getInfo();
        }
    }

    private static Lock lock = new ReentrantLock();
    private static int i = 0;

    private static int getInfo() {
        lock.lock();
        try {

            Thread.sleep(5000);
            // 子线程没执行 
            i++;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            System.out.println("释放锁");
        }
        return i;
    }

  • 结果分析:stop的调用,虽然释放了锁,但是方法内的关键操作没有执行,会导致随机结果。
释放锁
释放锁
1

volatile + interrupt安全终止线程

 public static void main(String[] args) throws Exception{
        TestRunnable runnable = new TestRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(500);
        runnable.cancel();
        Thread.sleep(5);
        thread.interrupt();

    }

   private static class TestRunnable implements Runnable {

       private volatile boolean on = true;

       @Override
       public void run() {
           int i = 0;
           // 这里注意因为sleep清除标识位 不能使用Thread.interrupted()
           while (on && !Thread.currentThread().isInterrupted()) {
               System.out.println(i++);
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   // 中断异常处理

               }
           }
       }

       public void cancel() {
           on = false;
       }
   }

小结

  • 中断的基本概念和必要性
  • 如何终止线程

参考

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

未经允许不得转载:搜云库技术团队 » 基础复盘 - 线程中断

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

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

联系我们联系我们