如何正确停止线程

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

相关推荐
Daniel 大东7 分钟前
BugJson因为json格式问题OOM怎么办
java·安全
Theodore_10224 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
冰帝海岸5 小时前
01-spring security认证笔记
java·笔记·spring
世间万物皆对象6 小时前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
没书读了6 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
小二·6 小时前
java基础面试题笔记(基础篇)
java·笔记·python
开心工作室_kaic7 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
懒洋洋大魔王7 小时前
RocketMQ的使⽤
java·rocketmq·java-rocketmq
武子康7 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
转世成为计算机大神7 小时前
易考八股文之Java中的设计模式?
java·开发语言·设计模式