记一次Stream并行流的Bug

前一段时间遇到一段很诡异的问题,模拟一下问题出现的现场。

public class StreamTest {

    static {
        int sum = IntStream.range(0, 100)
                .parallel()
                .map(i -> i)
                .sum();
        System.out.println("sum = " + sum);
    }

    public static void main(String[] args) {
    }

}

上述代码在运行的时候会产生死锁,代码并不会执行静态代码块的输出,也就说阻塞到Stream计算这个地方了。如果将Stream改为普通的串行流的话,代码则可以正常运行。那么问题就出现在了并行上面。

Stream的并行操作是通过ForkJoinPool来实现的,ForkJoinPool是一种特殊的线程池,通过对任务进行分割计算,结果合并来充分利用cpu多核性能,是一种特殊的线程池。Stream有一个最坑的地方就是,所有的并行操作都是用的同一个ForkJoinPool池,也就是ForkJoinPool.commPool()返回的这个池,可以通过下述代码来验证:

public static void main(String[] args) {
        IntStream.range(0,10).parallel().forEach(i-> System.out.println(Thread.currentThread().getName()));
    }

所以出现这个问题的关键不是在于Stream,而是在于多线程和类初始化。经我测试,以下代码仍然会产生同样的问题:

public class StreamTest {
    static {
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(500);
                System.out.println("StreamTest.static initializer");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    public static void main(String[] args) {
    }

}

经过查证发现类在未完成初始化时,静态代码块的其他线程操作会等待当前类初始化完成,然而当前类初始化又在等待其他线程操作的完成,于是产生死锁。

我的建议是不要在静态代码块中做任何线程相关的操作,经过我后续的查找,在Stack Overflow上面发现一个相同问题的解答,完美的解释了这个问题出现的原因:

https://stackoverflow.com/questions/34820066/why-does-parallel-stream-with-lambda-in-static-initializer-cause-a-deadlock

你可能感兴趣的:(java)