如何正确停止线程

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

相关推荐
2401_854391089 分钟前
城镇住房保障:SpringBoot系统功能概览
java·spring boot·后端
hummhumm11 分钟前
Oracle 第29章:Oracle数据库未来展望
java·开发语言·数据库·python·sql·oracle·database
wainyz20 分钟前
Java NIO操作
java·开发语言·nio
工业3D_大熊25 分钟前
【虚拟仿真】CEETRON SDK在船舶流体与结构仿真中的应用解读
java·python·科技·信息可视化·c#·制造·虚拟现实
lzb_kkk34 分钟前
【JavaEE】JUC的常见类
java·开发语言·java-ee
爬山算法1 小时前
Maven(28)如何使用Maven进行依赖解析?
java·maven
2401_857439691 小时前
SpringBoot框架在资产管理中的应用
java·spring boot·后端
怀旧6661 小时前
spring boot 项目配置https服务
java·spring boot·后端·学习·个人开发·1024程序员节
李老头探索1 小时前
Java面试之Java中实现多线程有几种方法
java·开发语言·面试
芒果披萨2 小时前
Filter和Listener
java·filter