Java 执行器:如何停止已提交的任务?
-
07-07-2019 - |
题
我已经使用执行程序提交了一项任务,我需要它在一段时间后停止(例如5分钟)。我尝试过这样做:
for (Future<?> fut : e.invokeAll(tasks, 300, TimeUnit.SECONDS)) {
try {
fut.get();
} catch (CancellationException ex) {
fut.cancel(true);
tasks.clear();
} catch(ExecutionException ex){
ex.printStackTrace(); //FIXME: gestita con printstack
}
}
但我总是收到错误:我有一个共享向量,需要由任务修改,然后由线程读取,即使我停止所有任务,如果发生超时,我也会得到:
Exception in thread "Thread-1" java.util.ConcurrentModificationException
有什么不对?如何停止已提交的 5 分钟后仍在运行的任务?
解决方案
只因为你打电话 cancel()
在 Future
并不意味着任务会自动停止。您必须在任务中做一些工作以确保它会停止:
- 使用
cancel(true)
以便向任务发送中断。 - 处理
InterruptedException
. 。如果您的任务中的函数抛出InterruptedException
, ,确保在捕获异常后尽快优雅退出。 - 定期检查
Thread.currentThread().isInterrupted()
如果任务进行连续计算。
例如:
class LongTask implements Callable<Double> {
public Double call() {
// Sleep for a while; handle InterruptedException appropriately
try {
Thread.sleep(10000);
} catch (InterruptedException ex) {
System.out.println("Exiting gracefully!");
return null;
}
// Compute for a while; check Thread.isInterrupted() periodically
double sum = 0.0;
for (long i = 0; i < 10000000; i++) {
sum += 10.0
if (Thread.currentThread().isInterrupted()) {
System.out.println("Exiting gracefully");
return null;
}
}
return sum;
}
}
另外,正如其他帖子提到的: ConcurrentModificationException
即使使用线程安全也可以抛出 Vector
类,因为您从中获取迭代器 Vector
不是线程安全的,因此需要同步。高级 for 循环使用迭代器,因此请注意:
final Vector<Double> vector = new Vector<Double>();
vector.add(1.0);
vector.add(2.0);
// Not thread safe! If another thread modifies "vector" during the loop, then
// a ConcurrentModificationException will be thrown.
for (Double num : vector) {
System.out.println(num);
}
// You can try this as a quick fix, but it might not be what you want:
synchronized (vector) { // "vector" must be final
for (Double num : vector) {
System.out.println(num);
}
}
其他提示
ConcurrentModificationException
来自您对 tasks.clear()
的调用,而您的Exceutors正在迭代您的任务
Vector 代码>。您可以尝试做的是调用
shutdownNow()
ConcurrentModificationException
的最常见情况是 vector
在迭代的同时被修改。通常这将在一个线程中完成。您需要在整个迭代中对 Vector
进行锁定(并注意不要死锁)。
fut.get()是一个阻塞调用,即使在超时之后,你也会阻塞,直到任务完成。如果你想尽可能接近5分钟标记,你需要检查中断标志,我建议你使用Thread.isInterrupted()方法来保持中断状态。如果你想立即停止并且不需要清理任何状态,那么抛出一个将被Future捕获并作为ExecutionException指示给你的异常。
fut.cancel(true)没有做任何事情,因为invokeAll()方法已经为你做了这个。
除非你使用“任务”在其他地方收集,你可能不需要在上面调用clear()。这不会成为您的问题的根源,因为在您调用clear()时,使用List完成了invokeAll()方法。但是,如果您需要开始形成要执行的新任务列表,我建议您构建一个新的任务列表,而不是使用旧的新任务列表。
很遗憾,我的问题没有答案。我在这里没有看到足够的信息来诊断它。您提供的代码段中没有任何内容表示库类/方法的使用不当(仅限于不必要)。也许如果你包括一个完整的堆栈跟踪,而不是一行错误。
将 fut.cancel(true);
放入finally块