一个简单的场景:
private static final ExecutorService executor =
Executors.newCachedThreadPool();
public static void main(String... args) throws InterruptedException, ExecutionException {
Future<byte[]> f = null;
for (int i = 0; i < 10; i++) {
f = executor.submit(new Callable<byte[]>(){
@Override
public byte[] call() {
System.out.println("Starting task.");
try {
return new byte[1500 * 1024 * 1024]; // 1500 mb
}
finally {
System.out.println("Task complete.");
}
}
});
}
// System.out.println(f.get().length);
}
当我运行上面的代码时,它(据称)运行没有错误。
(奇怪的是,当我在NetBeans中分析此代码时,会抛出 OutOfMemoryError ,但在我正常运行时却不会抛出。)
全部10个“任务完成。”消息立即显示,在一个太短的时间范围内,实际分配的字节数组太短。
当我取消注释最后一行时,抛出 ExecutionException 。
我知道代码示例有点荒谬...... 但是为什么没有异常被抛出,我怎么能让 OutOfMemoryError 出现呢?我必须抓住它吗?这是安全的操作吗?
答案 0 :(得分:3)
你不应该抓住Errors
- 他们的目的是吵闹你的程序。如果a,您应该只捕获Exception
。你正在记录它/重新投掷它或b。你正在处理它;你没办法处理OutOfMemoryError
,所以让你的程序崩溃就像它应该的那样。
“任务完成”在分配Future
个对象时显示,而不是在他们完成工作时显示 - 您需要在每个Future上调用f.get()
以确保它已完成分配它的字节数组。当您对其进行性能分析时,程序运行速度较慢,这允许更多Futures
在Main方法终止之前分配其字节数组,这反过来又允许它们耗尽所有堆空间。
将f
更改为期货的ArrayList(以及f = executor.submit
至f.add(executor.submit)
),然后迭代它并在其所有期货上调用.get()
。这应该在不使用探查器的情况下触发OutOfMemoryError。
答案 1 :(得分:0)
主线程不会自动识别线程引发的异常抛出。 使用了以前的run方法,它不会抛出异常而你被迫使用uncaughtexceptionhandler。
引入了JDK1.5期货和期货。 callables的调用方法抛出异常并返回值。现在要捕获call方法抛出的异常,你需要调用future.get()。
所以你的代码工作正常。