【问题标题】:Why are methods after interruption of a thread not called?为什么不调用线程中断后的方法?
【发布时间】:2021-03-19 23:28:00
【问题描述】:

我想优雅地关闭一个线程。但是,一旦启动关闭,线程应该在结束正常操作后执行一些关闭操作。

两个线程都使用睡眠和/或等待并处理 InterruptedException,它们还在循环中处理任务,只需几毫秒。所以我希望 while 循环结束,因为 Thread.currentThread().isInterrupted() 变为“真”。

问题是我的代码有时会得到日志“SHUTDOWN”,有时却没有。我也只是有时会得到“中断”,这是我当然理解的。对于另一个类似的线程,我永远不会得到“SHUTDOWN”。

ExecutorService executor = Executors.newFixedThreadPool(2);

executor.execute(new Test());

Thread.sleep(10000);

executor.shutdown();
        
try {
    if(this.executor.awaitTermination(60, TimeUnit.SECONDS)) {
        this.loggerFactory.getLogger(this.getClass()).info("CLOSED (GRACEFULLY)!");
    }  else {
        this.executor.shutdownNow();
        
        this.loggerFactory.getLogger(this.getClass()).info("CLOSED (IMMEDIATELY)!");
    }
} catch(InterruptedException e) {
    this.executor.shutdownNow();
            
    this.loggerFactory.getLogger(this.getClass()).info("CLOSED (IMMEDIATELY)!");
}
class Test implements Runnable {

    private volatile boolean isRunning = true;

    @Override
    public void run() {
        try {
            while(!Thread.currentThread().isInterrupted()) {
                while(!this.isRunning) {
                    synchronized(this) {
                        this.wait();
                    }
                }
                
                // DO SOMETHING LASTING A FEW MILLISECONDS
                    
                Thread.sleep(500);
            }
        } catch(InterruptedException e) {
            this.loggerFactory.getLogger(this.getClass()).info("INTERRUPTED!");
        }
        
        this.loggerFactory.getLogger(this.getClass()).info("SHUTDOWN!");

        // DO SOME SHUTDOWN OPERATION
    }

}

【问题讨论】:

  • 所以你希望线程总是执行一些关闭操作,而不管它们是如何被“告知”停止的?
  • 这是正确的。
  • 在我看来,可能抛出了一些你没有看到的异常。将任务缩小并从中移除关闭活动会很好。
  • @Martin 谁将 isRunning 设置为 false?
  • @dreamcrash 一个停止方法。但这与我的问题无关,也没有使用。实际上还有一个 start 方法将其设置为 true。

标签: java multithreading


【解决方案1】:

编辑:

经过 OP 的一些评论,似乎有一个完全不同且更优越的解决方案:

使用钩子!

Java 有一个系统来“安装”一个关闭挂钩。这些在VM关闭时被调用......有时。如果您收到 SIGTERMed (kill -9) 或有人绊倒了电源线,或者 linux 由于内存使用过多、内核转储或您的 VM 硬崩溃(例如,本机代码中的核心转储)而终止了您的进程,或者设备断电,它们当然不会被调用。

但是,如果进程中有人运行System.exit(),或者所有非守护线程都已完成,或者有人按下 CTRL+C 或向您的进程发送 SIGKILL(kill,而不是 kill -9),它们就会运行首先,只有当它们都完成后,java 进程才会真正结束。

这听起来像是一个非常优越的解决方案。您的关闭挂钩应该:

  1. 获取一些私有 AtomicBoolean 的锁定。
  2. 将布尔值设置为 false(布尔值表示:我可以查询此传感器吗?)
  3. 释放锁。
  4. 重置传感器。
  5. 返回。

所有读取该传感器的正常操作代码都应该:

  1. 获取布尔值的锁定。
  2. 如果为 false,则抛出或以其他方式中止。
  3. 执行传感器读取操作。
  4. 释放锁。

在没有锁住的情况下,任何东西都不能接触那个传感器(如果不这样做,可能会在你已经重置它之后弄乱那个传感器,这很糟糕)。


原答案:

我想优雅地关闭一个线程。

为什么? “优雅地”是一个很好听的词,但一旦你深入了解它的含义,它就是令人讨厌的东西。这个词的意思是:“如果有人绊倒电源线或我的应用程序硬崩溃,这将导致我的软件可能持续失败(例如,如果不清理东西就无法启动)”。

一个更好的设计是有一个不需要关闭的线程。只需拔下插头,一切都很好。

例如,旧的文件系统(MS-DOS 和早期的 Windows 时代)需要正常关闭;不这样做会导致持续的问题——系统根本无法启动,你把盒子弄砖了。然后他们有了缓解系统(chkdsk 系统),但现代操作系统要好得多。他们的文件系统处理设置大多不关心被“优雅地”关闭。只要拔掉它们的插头,它们就会没事的,这就是期刊所做的。

So that I expected the while loop to end because Thread.currentThread().isInterrupted() becomes "true".

这不是您应该使用该 API 的方式。

以下是中断 API 的基本功能:

  1. 任何线程都可以在任何其他线程 (someThread.interrupt()) 上“提高中断标志”。

  2. 升旗除了升旗之外没有任何作用,除非方法明确决定查看它。

  3. Thread.interrupted() 方法是您应该如何读取标志以便对其采取行动,而不是 Thread.currentThread().isInterrupted()。前者将检查标志并清除它。后者只是检查标志。

  4. 一些 java 方法被指定来响应标志的上升。您认识这些方法是因为它们throws InterruptedException。可能有更多的方法;例如,在大多数操作系统上,中断当前等待更多字节从网络流入的线程(它们在从socket.getInputStream() 获得的InputStream 上的read() 调用上被阻塞)将导致读取调用失败(使用 IOException,而不是 InterruptedException,因为 read() 没有指定抛出 InterruptedEx),但这不能保证;在某些操作系统上,它不会,而且你不能打断它。

  5. 一般的策略是在你处理一个中断标志的那一刻,你降低这个标志,java代码就是这样做的:如果一个方法抛出InterruptedEx,这个标志将被清除。

  6. Java 没有定义如果被中断应该做什么。线程不会被神奇地中断;例如,当您的 VM 关闭时(有人按 CTRL+C),这不会中断任何线程。 Java 只会在所有线程上“拔掉插头”。那是因为这样更好(见上文)。因此,如果一个线程被中断,那是因为你在某个地方写了thread.interrupt(),因此,你决定它的含义。也许这意味着“重新读取配置文件并重新启动服务器侦听过程”。也许这意味着“停止计算国际象棋移动并执行迄今为止发现的最佳移动”。也许它的意思是“重新检查一个条件”。也许这意味着“完全结束线程”。由你决定。没有标准。

请注意,指定响应中断标志的各种方法(例如wait():它会抛出InterruptedException)都共享这个属性:如果您在标志上升时调用它们,它们将立即通过抛出InterruptedException返回,他们甚至从未开始等待。

因此,对于您的代码,鉴于您已经 wait(),只需创建 while(true) 并依赖 InterruptedEx。

【讨论】:

  • 很抱歉,我的系统实际上是一个嵌入式 Linux 系统,非常适合断电情况。我们对这个案子没有任何问题!我们需要这个的原因是因为我们有一个使用 I2C 总线连接到系统的传感器,我们需要重置传感器(因为通过软件关闭我们不会断电)。
  • 感谢您的回答,但您的第一部分有点奇怪!
  • 为什么很奇怪?好的,你有一个场景,如果你的嵌入式 linux 系统硬崩溃,那么你已经把设备变砖了。你不能在启动时重置传感器吗?关键是:最好不要优雅关闭,但是,嘿,有时,没有出路,也许你遇到了其中一种情况。这就是为什么答案的整个第二部分确实解释了中断的东西是如何工作的:P,但是,我将用一个更好的建议来编辑答案,等等。
  • 关于 6. 我们有一个 OSGi 环境,并且使用关闭挂钩很好地处理。这就是为什么 CTRL+C 确实允许关闭线程。
  • 不,如果系统掉电也没关系。但是,如果我们在某些情况下重新启动应用程序,连接的传感器的 I2C 总线处于不良状态或应该设置为休眠(这也与我们使用 I2C 总线通信有关)。
猜你喜欢
  • 1970-01-01
  • 2016-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-10
  • 2015-06-22
  • 2013-11-23
  • 1970-01-01
相关资源
最近更新 更多