文章目录

  • 1 讲解原理
  • 1.1 原理介绍:使用interrupt 来通知,而不是强制
  • 1.1.1 通常线程在什么情况下停止普通情况
  • 2 如何正确停止线程
  • 2.1 普通线程会在什么情况在停止普通情况,run方法内没有sleep或wait方法时,停止线程
  • 2.2 线程可能被阻塞的情况下,怎么停止线程
  • 2.3如果线程在每次迭代后都阻塞
  • 2.4while内try/catch的问题
  • 2.5实际中的两种最佳实践
  • 2.5.1 优先选择:传递中断
  • 2.5.2 不想或无法传递:恢复中断
  • 2.5.3 不应屏蔽中断
  • 3 停止线程的错误方法
  • 3.1stop:
  • 3.2 volative 设置boolean标记位
  • 4 重要函数的源码解析
  • 5 常见面试问题
  • 5.1 如何停止线程?
  • 5.2 如何处理不可中断的阻塞


1 讲解原理

1.1 原理介绍:使用interrupt 来通知,而不是强制

用户取消,或者服务突然关闭,或者超时 出错,都需要停止线程。
interrupt合作机制,用一个线程通知另一个线程停止工作。
停止不等于中断。Java中能做的就是告诉他该中断啦。自身线程具有决定权,不但能何时响应停止,何时中断停止,要不要停止。
所以没有能力去强制停止。

----》想要停止线程:如何正确的通知线程,以及被停止的线程如何去配合。

1.1.1 通常线程在什么情况下停止普通情况

1 run方法的所有代码都执行完毕
2 异常出现,且没有捕获

2 如何正确停止线程

2.1 普通线程会在什么情况在停止普通情况,run方法内没有sleep或wait方法时,停止线程

打印1000的倍数,到最大值的一半。一秒钟终端是什么效果
没有任何效果

/**
 * 描述:     run方法内没有sleep或wait方法时,停止线程
 */
public class RightWayStopThreadWithoutSleep implements Runnable {

    @Override
    public void run() {
        int num = 0;
        while ( num <= Integer.MAX_VALUE / 2) {
            if (num % 10000 == 0) {
                System.out.println(num + "是10000的倍数");
            }
            num++;
        }
        System.out.println("任务运行结束了");
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadWithoutSleep());
        thread.start();
        Thread.sleep(2000);
        thread.interrupt();
    }
}

改成

/**
 * 描述:     run方法内没有sleep或wait方法时,停止线程
 */
public class RightWayStopThreadWithoutSleep implements Runnable {

    @Override
    public void run() {
        int num = 0;
        while (!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2) {
            if (num % 10000 == 0) {
                System.out.println(num + "是10000的倍数");
            }
            num++;
        }
        System.out.println("任务运行结束了");
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadWithoutSleep());
        thread.start();
        Thread.sleep(2000);
        thread.interrupt();
    }
}

2.2 线程可能被阻塞的情况下,怎么停止线程

/**
 * 描述:     带有sleep的中断线程的写法
 */
public class RightWayStopThreadWithSleep {

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            int num = 0;
            try {
                while (num <= 300 && !Thread.currentThread().isInterrupted()) {
                    if (num % 100 == 0) {
                        System.out.println(num + "是100的倍数");
                    }
                    num++;
                }
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(500);
        thread.interrupt();
    }
}

e5 v4 关闭超线程 关闭 虚拟化 1230v3关闭超线程_System


当线程正在休眠的过程中,如果收到中断信号,他会响应中断,即抛出一个异常

2.3如果线程在每次迭代后都阻塞

/**
 * 描述:     如果在执行过程中,每次循环都会调用sleep或wait等方法,那么不需要每次迭代都检查是否已中断
 */
public class RightWayStopThreadWithSleepEveryLoop {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            int num = 0;
            try {
                while (num <= 10000) {
                    if (num % 100 == 0) {
                        System.out.println(num + "是100的倍数");
                    }
                    num++;
                    Thread.sleep(10);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(5000);
        thread.interrupt();
    }
}

2.4while内try/catch的问题

/**
 * 描述:     如果while里面放try/catch,会导致中断失效
 */
public class CantInterrupt {

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            int num = 0;
            while (num <= 10000 && !Thread.currentThread().isInterrupted()) {
                if (num % 100 == 0) {
                    System.out.println(num + "是100的倍数");
                }
                num++;
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(5000);
        thread.interrupt();
    }
}

e5 v4 关闭超线程 关闭 虚拟化 1230v3关闭超线程_停止线程_02


抛出异常,但是没有停止

Java 设计sllep理念:一旦响应中断,就会把线程的 interrupt 的标记位清除。

检查不到中断的迹象,就不能退出。

2.5实际中的两种最佳实践

2.5.1 优先选择:传递中断

/**
 * 描述:     最佳实践:catch了InterruptedExcetion之后的优先选择:在方法签名中抛出异常 那么在run()就会强制try/catch
 */
public class RightWayStopThreadInProd implements Runnable {

    @Override
    public void run() {
        while (true && !Thread.currentThread().isInterrupted()) {
            System.out.println("go");
            try {
                throwInMethod();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                //保存日志、停止程序
                System.out.println("保存日志");
                e.printStackTrace();
            }
        }
    }

    private void throwInMethod() throws InterruptedException {
            Thread.sleep(2000);
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadInProd());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
}

2.5.2 不想或无法传递:恢复中断

package threadcoreknowledge.stopthreads;

/**
 * 描述:最佳实践2:在catch子语句中调用Thread.currentThread().interrupt()来恢复设置中断状态,以便于在后续的执行中,依然能够检查到刚才发生了中断
 * 回到刚才RightWayStopThreadInProd补上中断,让它跳出
 */
public class RightWayStopThreadInProd2 implements Runnable {

    @Override
    public void run() {
        while (true) {
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("Interrupted,程序运行结束");
                break;
            }
            reInterrupt();
        }
    }

    private void reInterrupt() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadInProd2());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
}

2.5.3 不应屏蔽中断

e5 v4 关闭超线程 关闭 虚拟化 1230v3关闭超线程_ide_03

3 停止线程的错误方法

e5 v4 关闭超线程 关闭 虚拟化 1230v3关闭超线程_System_04

很难排除错误的所在

3.1stop:

package threadcoreknowledge.stopthreads;

/**
 * 描述:     错误的停止方法:用stop()来停止线程,会导致线程运行一半突然停止,没办法完成一个基本单位的操作(一个连队),会造成脏数据(有的连队多领取少领取装备)。
 */
public class StopThread implements Runnable {

    @Override
    public void run() {
        //模拟指挥军队:一共有5个连队,每个连队10人,以连队为单位,发放武器弹药,叫到号的士兵前去领取
        for (int i = 0; i < 5; i++) {
            System.out.println("连队" + i + "开始领取武器");
            for (int j = 0; j < 10; j++) {
                System.out.println(j);
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("连队"+i+"已经领取完毕");
        }
    }

    public static void main(String[] args) {
        Thread thread = new Thread(new StopThread());
        thread.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.stop();
    }
}

3.2 volative 设置boolean标记位

4 重要函数的源码解析

5 常见面试问题

5.1 如何停止线程?

5.2 如何处理不可中断的阻塞