如何正确停止线程

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

相关推荐
四谎真好看1 小时前
Java 黑马程序员学习笔记(进阶篇18)
java·笔记·学习·学习笔记
桦说编程1 小时前
深入解析CompletableFuture源码实现(2)———双源输入
java·后端·源码
java_t_t1 小时前
ZIP工具类
java·zip
lang201509282 小时前
Spring Boot优雅关闭全解析
java·spring boot·后端
pengzhuofan2 小时前
第10章 Maven
java·maven
百锦再3 小时前
Vue Scoped样式混淆问题详解与解决方案
java·前端·javascript·数据库·vue.js·学习·.net
刘一说3 小时前
Spring Boot 启动慢?启动过程深度解析与优化策略
java·spring boot·后端
壹佰大多3 小时前
【spring如何扫描一个路径下被注解修饰的类】
java·后端·spring
百锦再3 小时前
对前后端分离与前后端不分离(通常指服务端渲染)的架构进行全方位的对比分析
java·开发语言·python·架构·eclipse·php·maven
DokiDoki之父4 小时前
Spring—注解开发
java·后端·spring