Exception Java8并行流在抛出异常时的行为如何?
Java 8并行流在consuming子句中抛出异常时如何表现,例如在Exception Java8并行流在抛出异常时的行为如何?,exception,exception-handling,parallel-processing,java-8,java-stream,Exception,Exception Handling,Parallel Processing,Java 8,Java Stream,Java 8并行流在consuming子句中抛出异常时如何表现,例如在forEach处理中?例如,以下代码: final AtomicBoolean throwException = new AtomicBoolean(true); IntStream.range(0, 1000) .parallel() .forEach(i -> { // Throw only on one of the threads. if (throwExcepti
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) 在杀死除主线程以外的所有线程时,您也在杀死本应由这些线程处理的所有任务。所以这个数字应该在250左右,因为有1000个任务和4个线程,我假设返回3?:
int result = ForkJoinPool.getCommonPoolParallelism();
理论上有1000个任务,有4个线程,每个线程应该处理250个任务,然后你杀死其中的3个,意味着750个任务丢失。
还有250个任务要执行,ForkJoinPool将跨越3个新线程来执行这250个剩余任务
您可以尝试一些方法,如下更改流(使流不具有大小):
这一次,将有更多的操作结束,因为初始拆分索引未知,并由其他策略选择。您还可以尝试更改以下内容:
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());
你能以一些文件为基础吗?@AlikElzin kilaka不太可能,我认为这没有文件。我通过阅读其他一些关于这个bug的SO问题来记住这一点:@AlikElzin kilaka在核心libs dev邮件列表上还有一个讨论线程,它导致了Eugene提到的JBS bug。是一个较旧的引用。我添加了一个导致并行流不能提前返回的异常代码示例。相关:
} 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());