为什么线程中断后的方法没有被调用?

gajydyqb  于 2021-06-30  发布在  Java
关注(0)|答案(1)|浏览(210)

我想优雅地关闭一个线程。但是,一旦启动关闭,线程应该在结束常规操作后执行一些关闭操作。
这两个线程都使用sleep和/或wait和handleinterruptedexception,它们还处理循环中只需几毫秒的任务。所以我希望while循环结束,因为thread.currentthread().isinterrupted()变为“true”。
问题是,我的代码有时会出现“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
    }

}
htrmnn0y

htrmnn0y1#

编辑:
在op的一些评论之后,一个完全不同且更优越的解决方案似乎是可用的:

用钩子!

java有一个系统来“安装”关闭钩子。当虚拟机关闭时调用。。。有时候。如果你被解雇了( kill -9 )或者有人在电力电缆上绊倒了,或者linux由于内存过度使用而杀死了你的进程,或者内核转储,或者你的虚拟机硬崩溃(例如,本机代码中的核心转储),或者设备断电,当然,它们不会被调用。
但是,如果有人在这个过程中运行 System.exit() ,或者所有非守护进程线程都已完成,或者有人按ctrl+c或发送sigkill( kill ,不是 kill -9 )对于您的进程,它们首先运行,只有当它们全部完成时,java进程才真正结束。
这听起来是一个非常好的解决方案。关闭挂钩应:
获取某个私有原子布尔的锁。
将布尔值设置为false(布尔值表示:我可以查询此传感器吗?)
松开锁。
重置传感器。
返回。
读取传感器的所有正常操作代码应:
获取布尔值的锁。
如果为false,则抛出或以其他方式中止。
执行传感器读取操作。
松开锁。
任何东西都不应该在不握住锁的情况下接触传感器(如果不这样做,可能意味着在重置传感器之后弄乱了它,这将是不好的)。
原始答案:
我想优雅地关闭一个线程。
为什么?”“优雅”是一个听起来很不错的词,但一旦你深入了解它的含义,它就是一个讨厌的词。这个词的意思是:“如果有人被电缆绊倒或我的应用程序死机,可能会导致我的软件失败,可能是持续的(比如,不清理东西就不能再启动了)”。
更好的设计是拥有一个不需要关闭的线程。只要拔掉插头,一切就好了。
例如,旧的文件系统(ms-dos和早期的windows时代)需要正常关闭;如果不这样做,将导致持久性的问题-系统根本无法启动,你把盒子堵上了。然后他们有了缓解系统(chkdsk系统),但现代操作系统要好得多。他们的文件系统处理设置大多不关心被“优雅地”关闭。只要拔掉插头,他们就会没事的,这就是我的工作。 So that I expected the while loop to end because Thread.currentThread().isInterrupted() becomes "true". 你不应该这样使用api。
以下是中断api的基本功能:
任何线程都可以在任何其他线程上“提升中断标志”( someThread.interrupt() ).
升起旗子除了升起旗子之外什么都不做,除非一个方法显式地决定查看它。
方法 Thread.interrupted() 你应该如何读出旗子来对它采取行动,而不是 Thread.currentThread().isInterrupted() . 前者将检查旗帜并清除。后者只是检查旗帜。
一些java方法被指定来响应正在启动的标志。你认识这些方法是因为它们 throws InterruptedException . 可能有更多的方法;例如,在大多数操作系统上,中断当前正在等待更多字节从网络流入的线程(它们在网络上被阻止) read() 拜访某人 InputStream 获得自 socket.getInputStream() )将导致read调用失败(使用ioexception,而不是interruptedexception,因为read()没有指定抛出interruptedex),但这不能保证;在某些操作系统上,它不会,而且你不能打断它。
一般的策略是,在处理中断的标志时,降低该标志,java代码就是这样做的:如果一个方法抛出interruptedex,该标志将被清除。
java没有定义中断时应该做什么。线程不会被神奇地打断;例如,当您的虚拟机关闭时(有人点击ctrl+c),这不会中断任何线程。java只会“拔掉”所有线程的插头。这是因为这样更好(见上文)。因此,如果线程被中断,那是因为 thread.interrupt() 因此,在某个地方,你决定它的意义。可能意味着“重新读取配置文件并重新启动服务器侦听进程”。也许这意味着“停止计算象棋的招式,执行迄今为止最好的招式”。也许它的意思是“重新检查一个条件”。也许它的意思是“完全结束线程”。这取决于你。没有标准。
请注意,指定用于响应中断标志的各种方法(例如 wait() :它抛出interruptedexception)所有共享此属性:如果在标志打开时调用它们,它们将通过抛出 InterruptedException ,他们甚至从不开始等待。
所以,对于你的代码,假设你已经在wait()了,就这样做吧 while(true) 并依赖于interruptedex。

相关问题