记一次Stream并行流的Bug

本文记录了一次由于使用Stream并行流引发的死锁问题。当类在初始化过程中,静态代码块涉及线程操作时,可能会与其他线程产生依赖,导致死锁。Stream并行操作依赖于ForkJoinPool,而所有并行流默认使用同一线程池。为避免此类问题,建议不在静态代码块中进行线程相关操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值