zoukankan      html  css  js  c++  java
  • 使用interrupt停止线程 不能使用volatile标记位

    通常情况下我们不会手动的去停止一个线程,而是允许线程运行到结束自然停止,但是某些特殊情况,如用户突然退出程序或程序运行出错时,我们需要提前停止某些正在运行的线程。
     
    对于 Java 而言,最正确的停止线程的方式是使用 interrupt。但 interrupt 仅仅起到通知被停止线程的作用。而对于被停止的线程而言,它拥有完全的自主权,它既可以选择立即停止,也可以选择一段时间后停止,也可以选择压根不停止。那么为什么 Java 不提供强制停止线程的能力呢?
    Java 希望程序间能够相互通知、相互协作地管理线程,因为如果不了解对方正在做的工作,贸然强制停止线程就可能会造成一些安全的问题,为了避免造成问题就需要给对方一定的时间来整理收尾工作。比如:线程正在写入一个文件,这时收到终止信号,它就需要根据自身业务判断,是选择立即停止,还是将整个文件写入成功后停止,而如果选择立即停止就可能造成数据不完整,不管是中断命令发起者,还是接收者都不希望数据出现问题。
    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();
            //假设线程执行到这个方法,并且正在 sleep,此时有线程发送 interrupt 通知试图中断线程,就会立即抛出异常,并清除中断信号。抛出的异常被 catch 语句块捕捉。捕捉到异常的 catch 没有进行任何处理逻辑,相当于把中断信号给隐藏了,这样做是非常不合理的
            //当然,如果我们的线程收到终断请求可以直接停止,我们就可以不去捕获异常,让程序运行过程中直接去抛出异常,中断这个线程。
        
            // 这里至少做两件事:
            // 1、处理线程终止或者抛出终止异常
            // 2、 Thread.currentThread().isInterrupt() ;//这样做的目的是告诉线程后面的操作,这个线程已经终止了
                }
            };
            Thread thread = new Thread(runnable);
            thread.start();
            Thread.sleep(5);
            thread.interrupt();
        }
    我们一旦调用某个线程的 interrupt() 之后,这个线程的中断标记位就会被设置成 true。每个线程都有这样的标记位,当线程执行时,应该定期检查这个标记位,如果标记位被设置成 true,就说明有程序想终止该线程。回到源码,可以看到在 while 循环体判断语句中,首先通过 Thread.currentThread().isInterrupt() 判断线程是否被中断,随后检查是否还有工作要做。
     
    如果 sleep、wait 等可以让线程进入阻塞的方法使线程休眠了,而处于休眠中的线程被中断,那么线程是可以感受到中断信号的,并且会抛出一个 InterruptedException 异常,同时清除中断信号,将中断标记位设置成 false。这样一来就不用担心长时间休眠中线程感受不到中断了,因为即便线程还在休眠,仍然能够响应中断通知,并抛出异常。
    要求每一个方法的调用方有义务去处理异常。调用方要不使用 try/catch 并在 catch 中正确处理异常,要不将异常声明到方法签名中。如果每层逻辑都遵守规范,便可以将中断信号层层传递到顶层,最终让 run() 方法可以捕获到异常。而对于 run() 方法而言,它本身没有抛出 checkedException 的能力,只能通过 try/catch 来处理异常。层层传递异常的逻辑保障了异常不会被遗漏,而对 run() 方法而言,就可以根据不同的业务逻辑来进行相应的处理。
    之所以不能用volatile标记位声明boolean类型的标识来代替interrupt来停止线程也是这个道理,如果线程发生阻塞了,它将无法执行判断标识位的代码,阻塞无法收到停止线程的通知。线程依然不能停止。
  • 相关阅读:
    XtraGrid回车添加行,
    此行属于此表解决方案
    实践重于理论——创建一个监控程序探测WCF的并发处理机制
    使用SerialPort类设计串口通讯程序 (VS2005)[转载]
    WCF实例上下文基本内容概述
    DotNetBar教程
    MSChart使用做折线图饼图
    WCF Data Contract之KnownType
    WCF学习笔记之 搭建WCF技术知识框架
    C# SerialPort运行方式
  • 原文地址:https://www.cnblogs.com/tianhaichao/p/12166514.html
Copyright © 2011-2022 走看看