我在Executors.newFixedThreadPool(8)创建的8个ExecutorService线程中执行了5000个类似的Callable任务。每个任务都进入数据库以检索要处理的大量数据。
一切正常,99%的时间,但有时我看到在日志文件中一个很奇怪的执行日志信息,当DB缓慢或卡住(不要问为什么)和8个正在运行的任务停滞不前,尚未完成的所有8个线程,ExecutorService开始提交更多任务来逐个执行!
因此,日志显示在某些时刻ExecutorService变得疯狂并开始在等待队列中调用越来越多任务的Callable的call()方法,而无需等待先前的任务完成。越来越多的任务向DB发送请求,这最终使DB陷入困境,Java堆内存耗尽。
看起来在ExecutorService中发生了一些奇怪的事情,或者我对情况的理解是错误的。有没有人见过这样的东西?
我的脑堆溢出
P.S。这是来自Java API的引用:
Executors.newFixedThreadPool(int nThreads)
创建一个重用固定数量的线程的线程池 关闭共享的无界队列。在任何时候,最多nThreads线程 将是主动处理任务。如果提交了其他任务 当所有线程都处于活动状态时,它们将在队列中等待直到a 线程可用。 如果任何线程因故障而终止 在关闭之前执行期间,如果是,新的将取代它 需要执行后续任务。
难道这实际上发生,我的任务导致线程死亡和ExecutorService的创建多个线程并提交新的8个任务给他们,他们死和ExecutorService的创建8个更多的线程,并提交更多的任务8?
ps.s.s。:Callable的call()内部的整个操作都被try catch包围,所以如果我的操作中发生任何异常,将捕获并记录异常。这一切都没有发生。调用该调用并且永远不会返回,而下一个任务是逐个调用的,并且永远不会返回,永远不会完成,也不会抛出任何异常。
我怀疑我的任务会导致线程池中的线程死掉。怎么可能模仿?
答案 0 :(得分:3)
我也会猜测:
I/O
。Lock Wait Timeout
个例外。请注意,任务中的例外不会停止ExecutorService
。它只是将该任务标记为已完成并继续。
见这个例子:
public class Foo {
static class Task implements Callable<String> {
private static AtomicInteger i = new AtomicInteger(1);
public String call() throws Exception {
i.incrementAndGet();
if (i.get() % 2 != 0) {
throw new RuntimeException("That's odd, I failed.");
}
return "I'm done";
}
}
public static void main(String[] args) throws Exception {
ExecutorService es = Executors.newFixedThreadPool(2);
List<Future<String>> futures = new ArrayList<Future<String>>();
for (int i = 0; i < 5; i++) {
futures.add(es.submit(new Task()));
}
for (Future<String> future : futures) {
try {
System.out.println(future.get());
} catch (ExecutionException ee) {
System.err.println(ee.getCause());
}
}
es.shutdown();
}
}
可能的输出:
I'm done
I'm done
I'm done
java.lang.RuntimeException: That's odd, I failed.
java.lang.RuntimeException: That's odd, I failed.
答案 1 :(得分:0)
这只是猜测,(我认为鉴于问题中代码不足,我认为这是值得的):
如果当前任务抛出异常, ExecutorService.invokeAll(Collection<? extends Callable<T>> tasks)
将继续执行其他任务。 (你使用的是invokeAll()吗?我认为submit(Callable<T> task)
具有相同的行为,但是从javadoc中看不清楚)
在后续任务开始运行之前,您是否可以检查那些“卡住”任务变为Future.isDone()
?潜在的异常被抛出而在日志中看不到......
来自javadoc:
请注意,已完成的任务可能已正常终止或通过 抛出异常。
如果 这个案例,你可以抓住&amp;记录Callable.call()
方法定义中的所有异常。
HTH