我正在使用Java ExecutorService框架来提交可执行的可调用任务。 这些任务与Web服务通信,并且应用了5分钟的Web服务超时。 但是我已经看到,在某些情况下,超时被忽略并且线程'挂起'在API调用上 - 因此,我想取消所有花费的时间超过5分钟的任务。
目前,我有一个期货清单,我会遍历它们并调用future.get直到所有任务完成。现在,我已经看到future.get重载方法占用超时并在任务未在该窗口中完成时抛出超时。所以我想到了一种方法,我使用超时执行future.get(),并且在TimeoutException的情况下,我执行future.cancel(true)以确保此任务被中断。
我的主要问题
1.超时的获取是否是解决此问题的最佳方法?
2.是否有可能我正在等待一个尚未放置在线程池上的get调用(不是活动工作者)。在这种情况下,我可能正在终止一个线程,当它启动时可能在规定的时限内实际完成?
任何建议都将深表感谢。
答案 0 :(得分:2)
延迟获取是否是解决此问题的最佳方法?
是否有可能我正在等待一个尚未放置在线程池上的get调用(不是活动工作者)。在这种情况下,我可能正在终止一个线程,当它启动时可能在规定的时限内实际完成?
当您的任务包含不可中断阻止时,以下代码段可能是让您的任务响应中断的方式之一。它也不会取消未安排运行的任务。这里的想法是通过关闭套接字,数据库连接等来覆盖中断方法和关闭运行任务。这段代码并不完美,您需要根据需求进行更改,处理异常等。
class LongRunningTask extends Thread {
private Socket socket;
private volatile AtomicBoolean atomicBoolean;
public LongRunningTask() {
atomicBoolean = new AtomicBoolean(false);
}
@Override
public void interrupt() {
try {
//clean up any resources, close connections etc.
socket.close();
} catch(Throwable e) {
} finally {
atomicBoolean.compareAndSet(true, false);
//set the interupt status of executing thread.
super.interrupt();
}
}
public boolean isRunning() {
return atomicBoolean.get();
}
@Override
public void run() {
atomicBoolean.compareAndSet(false, true);
//any long running task that might hang..for instance
try {
socket = new Socket("0.0.0.0", 5000);
socket.getInputStream().read();
} catch (UnknownHostException e) {
} catch (IOException e) {
} finally {
}
}
}
//your task caller thread
//map of futures and tasks
Map<Future, LongRunningTask> map = new HashMap<Future, LongRunningTask>();
ArrayList<Future> list = new ArrayList<Future>();
int noOfSubmittedTasks = 0;
for(int i = 0; i < 6; i++) {
LongRunningTask task = new LongRunningTask();
Future f = execService.submit(task);
map.put(f, task);
list.add(f);
noOfSubmittedTasks++;
}
while(noOfSubmittedTasks > 0) {
for(int i=0;i < list.size();i++) {
Future f = list.get(i);
LongRunningTask task = map.get(f);
if (task.isRunning()) {
/*
* This ensures that you process only those tasks which are run once
*/
try {
f.get(5, TimeUnit.MINUTES);
noOfSubmittedTasks--;
} catch (InterruptedException e) {
} catch (ExecutionException e) {
} catch (TimeoutException e) {
//this will call the overridden interrupt method
f.cancel(true);
noOfSubmittedTasks--;
}
}
}
}
execService.shutdown();
答案 1 :(得分:0)
超时获取是解决此问题的最佳方法吗?
是的,对于Future对象get(timeout)
完全没问题,如果未来指向的任务已经执行,它将立即返回。如果任务尚未执行或正在执行,那么它将等到超时并且是一个很好的做法。
是否有可能在等待任务的get调用 尚未被放置在线程池中(不是活跃的工作者)
只有在线程池上放置任务时才会得到Future
对象,因此无法在任务上调用get()
而不将其放在线程池上。是的,有可能自由工作者尚未完成任务。
答案 2 :(得分:0)
你说的方法还可以。但最重要的是,在设置超时阈值之前,您需要知道线程池大小和环境的最佳值是什么。做一个压力测试,它会揭示你配置为Threadpool一部分的工作线程是否合适。这甚至可以减少超时值。所以这个测试对我来说是最重要的。
get的超时完全正常但是如果它抛出TimeoutException,你应该添加取消任务。如果您正确执行上述测试并将线程池大小和超时值设置为理想值,则甚至可能无需在外部取消任务(但您可以将其作为备份)。是的,有时在取消任务时,您可能最终取消执行者尚未接收的任务。
答案 3 :(得分:0)
您当然可以使用
取消任务task.cancel(真)
这完全合法。但是如果它是“RUNNING”,这会中断线程。
如果线程正在等待获取内部锁定,那么除了设置线程的中断状态之外,“中断”请求没有任何效果。 在这种情况下,你无法做任何事情来阻止它。为了中断发生,线程应该通过获取它正在等待的锁来从“阻塞”状态出来(可能需要超过5个分钟)。这是使用“内在锁定”的限制。
但是,您可以使用显式锁定类来解决此问题。您可以使用“Lock”接口的“lockInterruptibly”方法来实现此目的。 “lockInterruptibly”将允许线程尝试获取锁定,同时保持对中断的响应。以下是实现这一目标的一个小例子:
public void workWithExplicitLock()throws InterruptedException{
Lock lock = new ReentrantLock();
lock.lockInterruptibly()();
try {
// work with shared object state
} finally {
lock.unlock();
}
}