我有一个线程可能会卡住并永远运行。因此,经过一段时间后,我希望它停止执行,转到 finally 方法进行清理,然后死掉。我将如何安全地做到这一点?谢谢。
我对如何做到这一点的第一个想法是创建一个子线程并进行睡眠,然后进行清理。但是当父线程仍在尝试运行并且它不能运行时,它会输出错误。
我有一个线程可能会卡住并永远运行。因此,经过一段时间后,我希望它停止执行,转到 finally 方法进行清理,然后死掉。我将如何安全地做到这一点?谢谢。
我对如何做到这一点的第一个想法是创建一个子线程并进行睡眠,然后进行清理。但是当父线程仍在尝试运行并且它不能运行时,它会输出错误。
将您的代码重构为 Callable 并使用 ExecutorService 来获取 Future。然后使用 get 超时,如果到那时还没有完成,则会引发 TimeoutException。有关完整示例,请参阅https://stackoverflow.com/a/2275596/53897。
您需要为阻塞调用设置超时。如果没有超时,抽象调用并以这种方式超时。
您可以创建 1 个线程来轮询任务的完成状态,如果超过某个值则将其终止。任务本身仍然需要另一个线程。我会通过创建有价值的任务来做到这一点staleness
。定期轮询所有任务,如果它们过时,请取消它们。
建议 1:如果您将代码放在带有 wait() 语句的 try 块中,您可以捕获 interruptedException,然后您的 finally. 当环境需要中断你的线程时,另一个线程将不得不发送一个 notify() 或 notifyAll() 来引起中断。
建议 2:我只是 Java 的初学者,但线程卡住意味着您必须能够在 try/finally 块中引发自定义异常。
(1)
最好的解决方案是超时发送数据。应该看起来像
try {
mySendingDataLibraryApi.sendData(data, timeout /*, timeUnit */);
// some new APIs enable also to configure the time unit of the required timeout.
// Older APIs typically just use milliseconds.
} catch (TimeoutException e) {
doCleanup(); // your cleanup method.
}
(2)
如果这不适用,因为您使用的 API 没有公开此类配置,那么第二好的解决方案是使用可中断的 APIsendData
方法并中断正在执行的线程。这依赖于提供了这样一个可中断的 API 的事实。如果 API 没有提供定时方法,我不会太指望这种方法的存在......无论如何,执行任务的线程中的代码看起来像:
class MySendingDataRunnable implements Runnable {
@Override
public void run() {
try {
mySendingDataLibraryApi.sendDataInterruptibly(data);
} catch (InterruptedException e) {
doCleanup(); // your cleanup method.
// here either re-throw InterruptedExecption
// or restore the interrupted state with Thread.currentThread().interrupt();
}
}
}
调用者线程中的代码应使用ExecutorService及其方法返回的Future实例Future<?> submit(Runnable task)
,以便等待所需时间并取消任务,并将mayInterruptIfRunning
参数设置为true
:
final ExecutorService executor = Executors.newSingleThreadExecutor();
final Future<?> future = executor.submit(new MySendingDataRunnable());
try {
final Object noResult = future.get(60, TimeUnit.SECONDS); // no result for Runnable
} catch (InterruptedException e) {
// here again either re-throw or restore interupted state
} catch (ExecutionException e) {
// some applicative exception has occurred and should be handled
} catch (TimeoutException e) {
future.cancel(true); // *** here you actually cancel the task after time is out
}
(3)
如果您使用的 API 不提供这些功能(定时/可中断方法),您将不得不发挥您的创造力!你的这一行阻塞代码一定是阻塞了某些资源。尝试获取此资源并将其关闭或断开连接,从而隐式导致任务终止。一个典型的例子是关闭网络连接。
注意:上述解决方案仅提供了一种实际取消任务并释放线程以进行进一步任务的方法。虽然线程可能仍然存在。杀死线程通常不是您在任务完成(或就此失败)时执行的操作。当您为不应再次执行的特定任务创建了一些线程时,这是可以接受的。在这种情况下,您可以使用上述方法ExecutorService
并调用其shutdownNow()
方法。甚至shutdownNow()
只尽最大努力,通常取决于可中断的实际任务......
这是一篇详细的文章(有些旧但仍然如此)。