避免在多线程代码中使用关闭标志



我有以下代码:

private static final AtomicBoolean shutdown = new AtomicBoolean(false);
public static void main(final String... args) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
shutdown.set(true);
executorService.shutdown();
try {
executorService.awaitTermination(SHUTDOWN_TIMEOUT.getSeconds(), TimeUnit.SECONDS);
} catch (InterruptedException e) {
executorService.shutdownNow();
}
}));
executorService = Executors.newFixedThreadPool(2);
for (int i = 0; i < 2; i++) {
executorService.execute(create());
}
}
private static Runnable create() {
return new Runnable() {
@Override
public void run() {
while (!shutdown.get()) {
try {
Thread.sleep(5000);
System.out.println("Hatella" + Thread.currentThread().getName());
} catch (Throwable t) {
}
}
}
};
}

这段代码工作得很好,但我想以更简单的方式编写这段代码,这样我就不必在每个 while 循环中检查关闭标志状态。任何想法我能做些什么来解决这个问题并实现同样的事情。

shutdown()只会使ExecutorService不接受更多任务,但它将继续执行所有待处理的任务直到最后。由于您实际上想停止执行任务,因此首先应该使用shutdownNow(),这将发送中断信号。

public static void main(final String... args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
executorService.shutdownNow();
try {
executorService.awaitTermination(
SHUTDOWN_TIMEOUT.getSeconds(),TimeUnit.SECONDS);
} catch (InterruptedException e) {}
}));
for (int i = 0; i < 2; i++) {
executorService.execute(create());
}
}
private static Runnable create() {
return () -> {
while(!Thread.interrupted()) {
try {
Thread.sleep(5000);
System.out.println("Hatella" + Thread.currentThread().getName());
}
catch(InterruptedException ex) {
break;
}
catch (Throwable t) {
}
}
System.out.println("thread exit " + Thread.currentThread().getName());
};
}

中断标志不仅可以通过Thread.interrupted()查询,还会让Thread.sleep(…)等阻塞动作提前终止,通过InterruptedException上报情况。在这两种情况下,当Thread.interrupted()返回true或抛出InterruptedException时,中断状态将被重置,因此立即做出反应或记住您收到了中断状态至关重要。所以在上面的例子中,catch(InterruptedException ex)包含一个break,以结束循环。

但如图所示,中断不会终止线程,而是允许对其做出反应,例如在必要时在退出之前进行清理。

请注意,当唯一冗长的操作是阻塞操作时,您根本不需要手动轮询中断状态,例如以下内容也可以:

private static Runnable create() {
return () -> {
while(true) {
try {
Thread.sleep(5000);
System.out.println("Hatella" + Thread.currentThread().getName());
}
catch(InterruptedException ex) {
System.out.println("got "+ex+", "+Thread.interrupted());
break;
}
catch (Throwable t) {
}
}
System.out.println("thread exit");
};
}

由于此代码不会通过Thread.interrupted()检查和重置中断状态,信号将持续到下一次调用Thread.sleep,这将很快出现为立即响应,因为在两个sleep调用之间执行的代码很短。

a) 参见 在 Java 中将 ExecutorService 转换为守护进程。守护进程线程将在技术上回答所述问题(不需要轮询"关闭"变量),但在任何有状态上下文中都可能是一个主意,因为线程将在操作过程中停止,JVM 没有警告(一旦所有非守护进程线程完成)。

executorService = Executors.newFixedThreadPool(2, r -> {
Thread t = Executors.defaultThreadFactory().newThread();
t.setDaemon(true);
return t;
});

B) 现实世界中的另一种选择(空闲线程可能会阻塞/睡眠某物)是仅在InterruptedException时检查shutdown,这将发生在executorService.shutdownNow()

最新更新