gpt4 book ai didi

java - Future.cancel() 后跟 Future.get() 杀死我的线程

转载 作者:搜寻专家 更新时间:2023-11-01 02:19:12 25 4
gpt4 key购买 nike

我想使用 Executor 接口(interface)(使用 Callable)来启动一个 Thread(我们称之为可调用线程),它将做使用阻塞方法的工作。这意味着当主线程调用 Future.cancel(true)(调用 Thread.interrupt()InterruptedException>).

我还希望我的可调用线程在代码的取消部分使用其他阻塞方法中断时正确终止

在执行此操作时,我遇到了以下行为:当我调用 Future.cancel(true) 方法时,如果主线程立即等待,可调用线程会被正确通知中断但是对于使用 Future.get() 的终止,可调用线程在调用任何阻塞方法时都被杀死

以下 JUnit 5 片段说明了这个问题。如果主线程在 cancel()get() 调用之间不 hibernate ,我们可以很容易地重现它。如果我们睡了一会儿但还不够,我们可以看到可调用线程正在执行一半的取消工作。如果我们睡够了,可调用线程就会正确完成其取消工作。

注意 1:我检查了可调用线程的中断状态:正如预期的那样,它被正确设置了一次且仅一次。

注意 2:在中断后逐步调试我的可调用线程时(当传递到取消代码时),我在进入阻塞方法时在几步之后“松开”它(没有 InterruptedException 似乎被抛出)。

    @Test
public void testCallable() {

ExecutorService executorService = Executors.newSingleThreadExecutor();

System.out.println("Main thread: Submitting callable...");
final Future<Void> future = executorService.submit(() -> {

boolean interrupted = Thread.interrupted();

while (!interrupted) {
System.out.println("Callable thread: working...");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
System.out.println("Callable thread: Interrupted while sleeping, starting cancellation...");
Thread.currentThread().interrupt();
}
interrupted = Thread.interrupted();
}

final int steps = 5;
for (int i=0; i<steps; ++i) {
System.out.println(String.format("Callable thread: Cancelling (step %d/%d)...", i+1, steps));
try {
Thread.sleep(200);
} catch (InterruptedException e) {
Assertions.fail("Callable thread: Should not be interrupted!");
}
}

return null;
});

final int mainThreadSleepBeforeCancelMs = 2000;
System.out.println(String.format("Main thread: Callable submitted, sleeping %d ms...", mainThreadSleepBeforeCancelMs));

try {
Thread.sleep(mainThreadSleepBeforeCancelMs);
} catch (InterruptedException e) {
Assertions.fail("Main thread: interrupted while sleeping.");
}

System.out.println("Main thread: Cancelling callable...");
future.cancel(true);
System.out.println("Main thread: Cancelable just cancelled.");

// Waiting "manually" helps to test error cases:
// - Setting to 0 (no wait) will prevent the callable thread to correctly terminate;
// - Setting to 500 will prevent the callable thread to correctly terminate (but some cancel process is done);
// - Setting to 1500 will let the callable thread to correctly terminate.
final int mainThreadSleepBeforeGetMs = 0;
try {
Thread.sleep(mainThreadSleepBeforeGetMs);
} catch (InterruptedException e) {
Assertions.fail("Main thread: interrupted while sleeping.");
}

System.out.println("Main thread: calling future.get()...");
try {
future.get();
} catch (InterruptedException e) {
System.out.println("Main thread: Future.get() interrupted: Error.");
} catch (ExecutionException e) {
System.out.println("Main thread: Future.get() threw an ExecutionException: Error.");
} catch (CancellationException e) {
System.out.println("Main thread: Future.get() threw an CancellationException: OK.");
}

executorService.shutdown();
}

最佳答案

当您在取消的Future 上调用get() 时,您将得到一个CancellationException,因此不会等待 Callable 的代码来执行它的清理。然后,您刚刚返回,当 JUnit 确定测试已完成时,观察到的线程被终止行为似乎是 JUnit 清理的一部分。

为了等待完全清理,将最后一行从

executorService.shutdown();

executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.DAYS);

请注意,在方法的 throws 子句中声明意外异常比使用调用 Assertions.failcatch 子句使测试代码困惑更简单>。 JUnit 无论如何都会将此类异常报告为失败。

然后,您可以删除整个sleep 代码。

可能值得将 ExecutorService 管理放入 @Before/@After 甚至 @BeforeClass/@AfterClass 方法,让测试方法不受此影响,专注于实际测试。¹


¹ 这些是 JUnit 4 名称。 IIRC,JUnit 5 名称类似于 @BeforeEach/@AfterEach resp。 @BeforeAll/@AfterAll

关于java - Future.cancel() 后跟 Future.get() 杀死我的线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54313537/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com