我想优雅地关闭一个线程。但是,一旦启动关闭,线程应该在结束常规操作后执行一些关闭操作。
这两个线程都使用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
}
}
1条答案
按热度按时间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。