[英]How do Java 8 parallel streams behave on a thrown exception?
Java 8并行流如何在forEach
子句中的抛出异常上运行,例如在forEach
处理中? 例如,以下代码:
final AtomicBoolean throwException = new AtomicBoolean(true);
IntStream.range(0, 1000)
.parallel()
.forEach(i -> {
// Throw only on one of the threads.
if (throwException.compareAndSet(true, false)) {
throw new RuntimeException("One of the tasks threw an exception. Index: " + i);
});
它会立即停止处理元素吗? 是否等待已经启动的元素完成? 是否等待所有流完成? 抛出异常后是否开始处理流元素?
什么时候回来? 异常后立即? 毕竟/部分元素是由消费者处理的?
在并行流引发异常后,是否继续处理元素? (发现这种情况发生的情况)。
这里有一般规则吗?
编辑 (15-11-2016)
试图确定并行流是否提前返回,我发现它不是确定的:
@Test
public void testParallelStreamWithException() {
AtomicInteger overallCount = new AtomicInteger(0);
AtomicInteger afterExceptionCount = new AtomicInteger(0);
AtomicBoolean throwException = new AtomicBoolean(true);
try {
IntStream.range(0, 1000)
.parallel()
.forEach(i -> {
overallCount.incrementAndGet();
afterExceptionCount.incrementAndGet();
try {
System.out.println(i + " Sleeping...");
Thread.sleep(1000);
System.out.println(i + " After Sleeping.");
}
catch (InterruptedException e) {
e.printStackTrace();
}
// Throw only on one of the threads and not on main thread.
if (!Thread.currentThread().getName().equals("main") && throwException.compareAndSet(true, false)) {
System.out.println("Throwing exception - " + i);
throw new RuntimeException("One of the tasks threw an exception. Index: " + i);
}
});
Assert.fail("Should not get here.");
}
catch (Exception e) {
System.out.println("Cought Exception. Resetting the afterExceptionCount to zero - 0.");
afterExceptionCount.set(0);
}
System.out.println("Overall count: " + overallCount.get());
System.out.println("After exception count: " + afterExceptionCount.get());
}
不从主线投掷时迟到返回 。 这导致在抛出异常后处理许多新元素。 在我的机器上,抛出异常后处理了大约200个元素。 但是,并非所有1000个元素都得到了处理。 那么这里的规则是什么? 为什么即使抛出异常也会处理更多元素?
删除not( !
)符号时提前返回 ,导致异常被抛出主线程。 只有已经启动的元素已完成处理,并且没有处理新的元素。 这里的情况很早就回来了。 与以前的行为不一致。
我在这里错过了什么?
当其中一个阶段抛出异常时,它不会等待其他操作完成,异常将被重新抛给调用者。 这就是ForkJoinPool处理它的方式。
相比之下,findFirst例如并行运行时,只有在所有操作完成处理后才会将结果呈现给调用者(即使在需要完成所有操作之前已知结果)。
换句话说:它将提前返回,但将完成所有正在运行的任务。
编辑回答最后的评论
霍尔格的答案(评论中的链接)对此进行了解释,但这里有一些细节。
1)当杀死所有BUT主线程时,你也杀死了应该由这些线程处理的所有任务。 所以,这个数字实际上应该多转转250有1000个任务,4个线程,我认为这将返回3?:
int result = ForkJoinPool.getCommonPoolParallelism();
理论上有1000个任务,有4个线程,每个应该处理250个任务,然后你杀死其中3个,意味着750个任务丢失。 剩下250个任务要执行,ForkJoinPool将跨越3个新线程来执行这250个左任务。
您可以尝试一些事情,像这样更改您的流(使流不大):
IntStream.generate(random::nextInt).limit(1000).parallel().forEach
这一次,将会有更多的操作结束,因为初始拆分索引是未知的,并由其他策略选择。 您还可以尝试改变这一点:
if (!Thread.currentThread().getName().equals("main") && throwException.compareAndSet(true, false)) {
对此:
if (!Thread.currentThread().getName().equals("main")) {
这次你总是会杀死除main之外的所有线程,直到某个点,ForkJoinPool不会创建新线程,因为任务太小而无法拆分,因此不需要其他线程。 在这种情况下,更少的任务将完成。
2)你的第二个例子,当你真正杀死主线程时,就像代码的方式一样,你将看不到其他线程的实际运行。 更改 :
} catch (Exception e) {
System.out.println("Cought Exception. Resetting the afterExceptionCount to zero - 0.");
afterExceptionCount.set(0);
}
// give some time for other threads to finish their work. You could play commenting and de-commenting this line to see a big difference in results.
TimeUnit.SECONDS.sleep(60);
System.out.println("Overall count: " + overallCount.get());
System.out.println("After exception count: " + afterExceptionCount.get());
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.