java Stream使用记录

本文深入探讨了Java中Stream并行处理的内部机制,详细解析了Stream如何通过ForkJoinPool实现并行计算,以及AbstractTask在并行处理中的核心作用。并通过实例对比了普通流与并行流在处理大量数据时的效率差异,强调了在并行流处理中避免修改外部变量的重要性。

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

java Stream使用记录
java中Stream本质上是把计算封装成一个任务,放到ForkJoin的commonPool中执行.
那么就看一下stream是扩展ForkJoin的结构:

Object (java.lang)
 ForkJoinTask (java.util.concurrent)
  CountedCompleter (java.util.concurrent)
   AbstractTask (java.util.stream)
    ReduceTask in ReduceOps (java.util.stream)
    CollectorTask in Nodes (java.util.stream)
      OfRef in CollectorTask in Nodes (java.util.stream)
	  OfDouble in CollectorTask in Nodes (java.util.stream)
	  OfLong in CollectorTask in Nodes (java.util.stream)
	  OfInt in CollectorTask in Nodes (java.util.stream)
	AbstractShortCircuitTask (java.util.stream)
	  MatchTask in MatchOps (java.util.stream)
	  SliceTask in SliceOps (java.util.stream)
      FindTask in FindOps (java.util.stream)

如上所示,我们发现AbstractTask是第一个扩展自ForkJoin原有结构抽象类:
如下:

@Override
public void compute() {
	Spliterator<P_IN> rs = spliterator, ls; // right, left spliterators
	long sizeEstimate = rs.estimateSize();
	long sizeThreshold = getTargetSize(sizeEstimate);
	boolean forkRight = false;
	@SuppressWarnings("unchecked") K task = (K) this;
	while (sizeEstimate > sizeThreshold && (ls = rs.trySplit()) != null) {//任务可以再次分割,分为左右两个计算节点
		K leftChild, rightChild, taskToFork;
		task.leftChild  = leftChild = task.makeChild(ls);
		task.rightChild = rightChild = task.makeChild(rs);
		task.setPendingCount(1);
		if (forkRight) {
			forkRight = false;
			rs = ls;
			task = leftChild;
			taskToFork = rightChild;
		}
		else {
			forkRight = true;
			task = rightChild;
			taskToFork = leftChild;
		}
		taskToFork.fork();//放到ForkJoin的commonPool的工作队列中执行
		sizeEstimate = rs.estimateSize();
	}
	task.setLocalResult(task.doLeaf());
	task.tryComplete();//合并操作
}

以ReduceTask举例:

public void onCompletion(CountedCompleter<?> caller) {//注意一点每个节点只会有一个线程在运行,不会并发
	if (!isLeaf()) {
		S leftResult = leftChild.getLocalResult();
		leftResult.combine(rightChild.getLocalResult());
		setLocalResult(leftResult);
	}
	// GC spliterator, left and right child
	super.onCompletion(caller);
}

下面是一点使用parallelStream流的心得:
请看下面的例子:

ArrayList<Integer> objects = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
	objects.add(i);
}
System.out.println(objects.size());

List<Integer> al = new LinkedList<>();
objects.parallelStream()
		.forEach(f -> al.add(f));
System.out.println(al.size());

List<Integer> collect = objects.parallelStream()
		.map(m -> m)
		.collect(Collectors.toList());
System.out.println(collect.size());

结果如下:

100000
70996
100000

在parallelStream中建议大家尽量不要再流处理过程中更改流以外的变量,如果想要使用请使用支持并发的类型.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值