如何正确停止线程

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方法中断,子线程就可以感受到中断信号,做出响应。

相关推荐
未若君雅裁6 小时前
MyBatis 一级缓存、二级缓存与清理机制
java·缓存·mybatis
AI人工智能+电脑小能手6 小时前
【大白话说Java面试题 第65题】【JVM篇】第25题:谈谈对 OOM 的认识
java·开发语言·jvm
阿维的博客日记7 小时前
Nacos 为什么能让配置动态生效?(涉及 @RefreshScope 注解)
java·spring
雨辰AI7 小时前
SpringBoot3 + 人大金仓读写分离 + 分库分表 + 集群高可用 全栈实战
java·数据库·mysql·政务
辰海Coding8 小时前
MiniSpring框架学习-完成的 IoC 容器
java·spring boot·学习·架构
小小编程路8 小时前
C++ 多线程与并发
java·jvm·c++
AI视觉网奇8 小时前
linux 检索库 判断库是否支持
java·linux·服务器
她的男孩9 小时前
从零搭一个企业后台,为什么我把能力拆成 Starter 和 Plugin
java·后端·架构
RainCity9 小时前
Java Swing 自定义组件库分享(七)
java·笔记·后端
Sam_Deep_Thinking9 小时前
连锁门店的外卖订单平台对接
java·微服务·架构·系统架构