How do Java 8 parallel streams behave on a thrown exception?

后端 未结 1 1820
傲寒
傲寒 2020-12-31 03:17

How do Java 8 parallel streams behave on a thrown exception in the consuming clause, for example in forEach handling? For example, the following code:



        
1条回答
  •  囚心锁ツ
    2020-12-31 03:55

    When an exception is thrown in one of the stages, it does not wait for other operations to finish, the exception is re-thrown to the caller. That is how ForkJoinPool handles that.

    In contrast findFirst for example when run in parallel, will present the result to the caller only after ALL operations have finished processing (even if the result is known before the need to finish of all operations).

    Put in other words : it will return early, but will leave all the running tasks to finish.

    EDIT to answer the last comment

    This is very much explained by Holger's answer (link in comments), but here are some details.

    1) When killing all BUT the main thread, you are also killing all the tasks that were supposed to be handled by these threads. So that number should actually be more around 250 as there are 1000 tasks and 4 Threads, I assume this returns 3?:

    int result = ForkJoinPool.getCommonPoolParallelism();
    

    Theoretically there are 1000 tasks, there are 4 threads, each supposed to handle 250 tasks, then you kill 3 of them meaning 750 tasks are lost. There are 250 tasks left to execute, and ForkJoinPool will span 3 new threads to execute these 250 left tasks.

    A few things you can try, change your stream like this (making the stream not sized):

    IntStream.generate(random::nextInt).limit(1000).parallel().forEach
    

    This time, there would be many more operations ending, because the initial split index is unknown and chosen by some other strategy. What you could also try is change this :

     if (!Thread.currentThread().getName().equals("main") && throwException.compareAndSet(true, false)) {
    

    to this:

     if (!Thread.currentThread().getName().equals("main")) {
    

    This time you would always kill all threads besides main, until a certain point, where no new threads will be created by ForkJoinPool as the task is too small to split, thus no need for other threads. In this case even less tasks would finish.

    2) Your second example, when you actually kill the main thread, as the way code is, you will not see the actual running of other threads. Change it :

        } 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());
    

    0 讨论(0)
提交回复
热议问题