如何正确停止线程

interrupt 停止线程

对于 Java 而言,最正确的停止线程的方式是使用 thread.interrupt();。Java 并不提供强制停止线程的能力。原因是:贸然强制停止线程可能会造成一些安全的问题。

java 复制代码
while (!Thread.currentThread().isInterrupted() && more work to do) {

    do more work

}

sleep 期间能否感受到中断

java 复制代码
public class StopDuringSleep {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            int num = 0;
            try {
                while (!Thread.currentThread().isInterrupted() && num <= 1000) {
                    System.out.println(num);
                    num++;
                    Thread.sleep(1000000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(5);
        thread.interrupt();
    }
}

如果线程在执行任务期间有休眠需求,主线程休眠 5 毫秒后,通知子线程中断,此时子线程正在执行 sleep 命令。 上面情况会有下面结果:

如果 sleep、wait 等让线程进入休眠状态,处于休眠状态的线程被中断,线程是可以感受到中断信号的,并且会抛出 InterruptedException异常,同时清除中断信号,将中断标记设置为 false。

所以最佳处理方式是方法签名抛出中断异常,将中断信号向上传递。

volatile 标记位停止线程是错误的

top()suspend()resume(),这些方法已经被 Java 直接标记为 @Deprecated。原因如下:

  • stop()方法会直接把线程停止,会导致数据完整性等问题。
  • suspend()方法不会释放 ,这把锁在线程被 resume()之前是不会被释放的, 容易导致死锁问题。

volatile 适用的场景

java 复制代码
public class VolatileCanStop implements Runnable {
    private volatile boolean canceled = false;
    @Override
    public void run() {
        int num = 0;
        try {
            while (!canceled && num <= 1000000) {
                if (num % 10 == 0) {
                    System.out.println(num + "是10的倍数。");
                }
                num++;
                Thread.sleep(1);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        VolatileCanStop r = new VolatileCanStop();
        Thread thread = new Thread(r);
        thread.start();
        Thread.sleep(3000);
        r.canceled = true;
    }
}

上面在主线程设置 volatile为 true 后,子线程就停止了。

volatile 修饰标记位不适用的场景

java 复制代码
class Producer implements Runnable {
    public volatile boolean canceled = false;
    
    BlockingQueue storage;
    public Producer(BlockingQueue storage) {
        this.storage = storage;
    }

    @Override
    public void run() {
        int num = 0;
        try {
            while (num <= 100000 && !canceled) {
                if (num % 50 == 0) {
                    // 假设这里阻塞了
                    storage.put(num);
                    System.out.println(num + "是50的倍数,被放到仓库中了。");
                }
                num++;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("生产者结束运行");
        }
    }
}

假设子线程在 storage.put(num);时被阻塞了,这时主线程设置 volatile变量为 true,子线程没办法进入下一次循环判断,所以并不会停止。但是如果使用 interrupt方法中断,子线程就可以感受到中断信号,做出响应。

相关推荐
JH30734 小时前
SpringBoot 优雅处理金额格式化:拦截器+自定义注解方案
java·spring boot·spring
Coder_Boy_5 小时前
技术让开发更轻松的底层矛盾
java·大数据·数据库·人工智能·深度学习
invicinble6 小时前
对tomcat的提供的功能与底层拓扑结构与实现机制的理解
java·tomcat
较真的菜鸟6 小时前
使用ASM和agent监控属性变化
java
黎雁·泠崖6 小时前
【魔法森林冒险】5/14 Allen类(三):任务进度与状态管理
java·开发语言
qq_12498707537 小时前
基于SSM的动物保护系统的设计与实现(源码+论文+部署+安装)
java·数据库·spring boot·毕业设计·ssm·计算机毕业设计
Coder_Boy_7 小时前
基于SpringAI的在线考试系统-考试系统开发流程案例
java·数据库·人工智能·spring boot·后端
Mr_sun.7 小时前
Day06——权限认证-项目集成
java
瑶山7 小时前
Spring Cloud微服务搭建四、集成RocketMQ消息队列
java·spring cloud·微服务·rocketmq·dashboard
abluckyboy8 小时前
Java 实现求 n 的 n^n 次方的最后一位数字
java·python·算法