我有以下代码:
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()