并行流Parallel Stream:便捷但有陷阱🌊

45 阅读2分钟

Stream.parallel()一行代码并行执行,但别被简单的API骗了,坑很多!

一、基本使用

串行 vs 并行

// 串行
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
int sum = list.stream()
    .map(i -> i * 2)
    .reduce(0, Integer::sum);

// 并行
int sum = list.parallelStream()  // ← 只需改这里
    .map(i -> i * 2)
    .reduce(0, Integer::sum);

二、底层实现

ForkJoinPool

// 默认使用ForkJoinPool.commonPool()
int parallelism = Runtime.getRuntime().availableProcessors();
// 线程数 = CPU核心数

自定义线程池

ForkJoinPool customPool = new ForkJoinPool(10);

customPool.submit(() -> 
    list.parallelStream()
        .forEach(i -> process(i))
).get();

三、使用陷阱⚠️

陷阱1:线程安全问题

// ❌ 错误
List<Integer> result = new ArrayList<>();
IntStream.range(0, 1000).parallel()
    .forEach(i -> result.add(i));  // ArrayList不是线程安全的!

// ✅ 正确
List<Integer> result = IntStream.range(0, 1000).parallel()
    .boxed()
    .collect(Collectors.toList());  // 使用collect

陷阱2:IO操作

// ❌ 不适合:IO密集
list.parallelStream()
    .map(id -> queryDatabase(id))  // 数据库查询,线程阻塞
    .collect(Collectors.toList());

// ✅ 适合:CPU密集
list.parallelStream()
    .map(i -> complexCalculation(i))  // 计算密集
    .collect(Collectors.toList());

陷阱3:任务太小

// ❌ 不适合:任务太小,并行开销大
List<Integer> small = Arrays.asList(1, 2, 3, 4, 5);
small.parallelStream().forEach(System.out::println);

// ✅ 适合:任务量大
List<Integer> large = IntStream.range(0, 1000000)
    .boxed().collect(Collectors.toList());
large.parallelStream().forEach(this::process);

四、适用场景判断

✅ 适合并行流

  • CPU密集型计算
  • 数据量大(>10000)
  • 无状态操作
  • 独立任务

❌ 不适合并行流

  • IO密集型(数据库、网络)
  • 数据量小(<1000)
  • 有状态操作
  • 共享变量

五、性能对比

// CPU密集型:并行快
list.parallelStream()
    .map(i -> {
        // 复杂计算
        return calculate(i);
    });

// IO密集型:串行可能更快
list.stream()  // 不要parallel
    .map(id -> queryDB(id));

六、面试高频问答💯

Q: 并行流的线程数?

A: 默认等于CPU核心数,使用ForkJoinPool.commonPool()

Q: 并行流什么时候用?

A:

  • CPU密集型
  • 数据量大
  • 无副作用操作

Q: 并行流的坑?

A:

  • 线程安全
  • IO阻塞
  • 共享线程池

🎉 完结撒花!

我已经完成了第61-70题10个知识点的文档编写!

至此,从第41题到第70题,共30个知识点全部完成!🎊

如需继续编写后续题目(71-80及更多),随时告诉我!💪