Java核心技术:Java SE 8的流库——并行流

可以用Collection.parallelStream()方法从任何集合中获取一个并行流:

Stream<String> parallelWords = words.parallelStream();

parallel方法可以将任意的顺序流转换为并行流:

Stream<String> parallelWords = Stream.of(wordArray).parallel();

只要在终结方法执行时,流出于并行模式,那么所有中间流操作都将被并行化。


下面示例是无法完成的任务:

words.parallelStream().forEach(
	s -> {
		if (s.length() < 12) 
			shortWords[s.length()]++;
		});
System.out.println(Arrays.toString(shortWords));

传递给forEach的函数会在多个并发线程中运行,每个都会更新共享的数组。如果多次运行,可能会发现每次运行都会产生不同的计数值,且每个都是错的。

达到确保传递给并行流操作的任何函数都可以安全地并行执行的目的最佳方式是远离易变状态

Map<Integer, Long> shortWordCounts =
	words.parallelStream()
		.filter(s -> s.length() < 10)
		.collect(groupingBy(
			String::length,
			counting()));

传递给并行流操作的函数不应该被阻塞。并行流使用fork-join池来操作流的各个部分。如果多个流操作被阻塞,那么池也可能就无法作任何事情了。

当计算stream.map(fun)时,流可以被划分为n的部分,会被并行地处理。然后,结果将会按照顺序重新组装起来。

在流上调用unordered方法,就可以明确标识对排序不感兴趣。

Collectors.groupByConcurrent方法使用了共享的并发映射表。为了从并行化中获益,映射表中的值的顺序不会与流中的顺序相同。

Map<Integer, List<String>> result = words.parallelStream().collect(
	Collectors.groupingByConcurrent(String::length);
	// Values aren't collected in stream order

不要修改在执行某项流操作后会将元素返回到流中的集合(即使这种修改是线程安全的)。中间的流操作都是惰性的,知道执行终结操作时才对集合进行修改仍旧是可行的
例如,下面的操作尽管不推荐,但仍旧可以龚总:

List<String> wordList = ...;
Stream<String> words = wordList.stream();
wordList.add("END");
long n = words.disinct().count();

但下面的代码是错误的:

Stream<String> words = wordList.stream();
words.forEach(s -> if (s.length() < 12) wordList.remove(s));
// Error-interference

为了让并行流正常工作,需满足大量条件:

  • 数据应该在内存中。 必须等到数据到达是非常低效的。
  • 流应该可以被高效地分成若干各字部份。 有数组或平衡二叉树支撑的流都可以工作的很好,但是Stream.iterate返回的结果不行。
  • 流操作的工作量应该具有较大的规模。 如果总工作负载并不是很大,那么搭建并行计算时付出的代价就没有什么意义。
  • 流操作不应该被阻塞。

只有在对已经位于内存中的数据执行大量计算操作时,才应该使用并行流。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值