Deadlock acontece se eu usar lambda no fluxo paralelo, mas isso não acontece se eu usar classe anônima? [duplicado

Esta pergunta já tem uma resposta aqui:

or que o fluxo paralelo com o lambda no inicializador estático causa um impass 3 respostas

O código a seguir leva a um impasse (no meu pc):

public class Test {
    static {
        final int SUM = IntStream.range(0, 100)
                .parallel()
                .reduce((n, m) -> n + m)
                .getAsInt();
    }

    public static void main(String[] args) {
        System.out.println("Finished");
    }
}

Mas se eu substituirreduce argumento lambda com classe anônima, não leva a um impasse:

public class Test {
    static {
        final int SUM = IntStream.range(0, 100)
                .parallel()
                .reduce(new IntBinaryOperator() {
                    @Override
                    public int applyAsInt(int n, int m) {
                        return n + m;
                    }
                })
                .getAsInt();
    }

    public static void main(String[] args) {
        System.out.println("Finished");
    }
}

Pode explicar essa situação?

P.S.

Encontrei esse código (um pouco diferente do anterior):

public class Test {
    static {
        final int SUM = IntStream.range(0, 100)
                .parallel()
                .reduce(new IntBinaryOperator() {
                    @Override
                    public int applyAsInt(int n, int m) {
                        return sum(n, m);
                    }
                })
                .getAsInt();
    }

    private static int sum(int n, int m) {
        return n + m;
    }

    public static void main(String[] args) {
        System.out.println("Finished");
    }
}

works não estáveis. Na maioria dos casos, ele trava, mas às vezes termina com êxito:

Eu realmente não sou capaz de entender por que esse comportamento não é estável. Na verdade, eu teste novamente o primeiro trecho de código e o mesmo comportamento. Portanto, o código mais recente é igual ao primeir

Para entender quais threads são usados, adicionei o seguinte "log":

public class Test {
    static {
        final int SUM = IntStream.range(0, 100)
                .parallel()
                .reduce((n, m) -> {
                    System.out.println(Thread.currentThread().getName());
                    return (n + m);
                })
                .getAsInt();
    }

    public static void main(String[] args) {
        System.out.println("Finished");
    }
}

Por exemplo, quando o aplicativo termina com êxito, vejo o seguinte log:

main
main
main
main
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
main
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
main
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
Finished
P.S. 2

Entendo que reduzir é suficiente operações complexas. Encontrei um exemplo mais simples para mostrar esse problema:

public class Test {
    static {
        System.out.println("static initializer: " + Thread.currentThread().getName());

        final long SUM = IntStream.range(0, 2)
                .parallel()
                .mapToObj(i -> {
                    System.out.println("map: " + Thread.currentThread().getName() + " " + i);
                    return i;
                })
                .count();
    }

    public static void main(String[] args) {
        System.out.println("Finished");
    }
}

para exemplo feliz (caso raro), vejo a seguinte saída:

static initializer: main
map: main 1
map: main 0
Finished

exemplo de caso feliz para faixa de fluxo estendida:

static initializer: main
map: main 2
map: main 3
map: ForkJoinPool.commonPool-worker-2 4
map: ForkJoinPool.commonPool-worker-1 1
map: ForkJoinPool.commonPool-worker-3 0
Finished

exemplo de caso que leva a um impasse:

static initializer: main
map: main 1

Isso também leva a um impasse, mas não a cada partid

questionAnswers(1)

yourAnswerToTheQuestion