数据集并行处理的方法和原理----并行处理数据与性能

本文介绍Java8中Stream接口的使用方法,包括声明性数据处理、内部迭代及并行流的应用。通过对比不同求和方法的性能,探讨并行流在多核处理器上的优势与局限。

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

Stream接口可以让你以声明的方式处理数据。同时,用内部迭代取代外部迭代能够让原生Java库控制流元素的处理。


Java7之前并行处理数据集合是一件很麻烦的事情:第一步:明确把包含数据的数据结构分成若干子部分;第二步,为每一个子部分分配一个线程;第三步,你需要恰当的时候对他们进行同步来避免不出现竞争的条件;第四步,等待所有线程完成,然后把这些部分结果合并起来。

在本文中你将看到,如何不花大力气来实现对数据集的并行处理。它允许你声明性地将顺序流变为并行流。流是如何在幕后利用JAVA7引入的分支/合并框架

对收集源调用parallelStream方法把集合转换为并行流。并行流就是把内容分为多个块,并用不同的线程分别处理每个数据块的流。这样一来,你就可以自动把给定操作的工作负荷分配给多核处理器的所有内核,让他们都忙起来。

package ExeriseAboutSreeam;

import java.util.stream.Stream;
/*高斯小时候发明了从1+2+3+...+100的简化公式,其实就是今天我们熟悉的等差数列求和问题
 * 现在我们用计算机编程来实现给定任何n,求1+2+3+...+n的和
 */
public class TheSum {
	//传统方法实现---外部迭代
	public static long iterativeSum(long n) {
		long result = 0;
		for(long i =1L; i<=result;i++) {
			result += i;
		}
		return result;
	}
	
	//利用流处理来实现----内部迭代
	public static long sequentialSum(long n) {
		return Stream.iterate(1L,i -> i+1)
				.limit(n)
				.reduce(0L,Long::sum);
	}
	
	//等一等,能不能提高一下计算的效率,假设您的电脑是多核的,怎么利用这太神机?
	public static long parallelSum(long n) {
		//利用并行流
		return Stream.iterate(1L, i -> i+1)
				.limit(n)
				.parallel()
				.reduce(0L, Long ::sum);
	}
}

如果,看不懂后面两种实现方法,请自行补习JAVA8关于Lambda表达式和流处理的相关内容。

现在,初步体会到并行流的强大作用,可以将数据集划分成若干块,然后利用多核进行多线程同步计算。这样将分配线程、合并结果等工作都抛给了计算机已有的框架。


效果如何?我们来测试一下,使用并行方式是不是更快?

package ExeriseAboutSreeam;

import java.util.function.Function;
import java.util.stream.LongStream;
import java.util.stream.Stream;
/*高斯小时候发明了从1+2+3+...+100的简化公式,其实就是今天我们熟悉的等差数列求和问题
 * 现在我们用计算机编程来实现给定任何n,求1+2+3+...+n的和
 */
public class TheSum {
	//传统方法实现---外部迭代
	public static long iterativeSum(long n) {
		long result = 0;
		for(long i =1L; i<=result;i++) {
			result += i;
		}
		return result;
	}
	
	//利用流处理来实现----内部迭代
	public static long sequentialSum(long n) {
		return Stream.iterate(1L,i -> i+1)
				.limit(n)
				.reduce(0L,Long::sum);
	}
	
	//等一等,能不能提高一下计算的效率,假设您的电脑是多核的,怎么利用这台神机?
	public static long parallelSum(long n) {
		//利用并行流
		return Stream.iterate(1L, i -> i+1)
				.limit(n)
				.parallel()
				.reduce(0L, Long ::sum);
	}
	//换一个数据结构
	public static long parallelRangedSum(long n) {
		return LongStream.rangeClosed(1,n)
				.parallel()
				.reduce(0L, Long ::sum);
				
	}
	
	
	//测试模块---测流量性能
	public long measureSumPerf(Function<Long, Long> adder, long n) {
		long fastest = Long.MAX_VALUE;
		for(int i =0; i<10 ; i++) {
			long start = System.nanoTime();
			long sum = adder.apply(n);
			long duration = (System.nanoTime() - start) /1_000_000;
			//System.out.println("结果:" + sum);
			if(duration < fastest) fastest = duration;
		}
		return fastest;
	}
	
	//主函数,测试时间;
	public static void main(String[] args) {
		TheSum theSum = new TheSum();
		System.out.println("方法1测试结果:" + 
		theSum.measureSumPerf(TheSum::iterativeSum, 10_000_000) + "msecs");
		
		System.out.println("方法2测试结果:" + 
		theSum.measureSumPerf(TheSum::sequentialSum, 10_000_000) + "msecs");
		
		System.out.println("方法3测试结果:" + 
		theSum.measureSumPerf(TheSum::parallelSum, 10_000_000) + "msecs");
		
		System.out.println("方法4测试结果:" + 
		theSum.measureSumPerf(TheSum::parallelRangedSum, 10_000_000) + "msecs");
		
	}
		
}

测试结果:

方法1测试结果:0msecs
方法2测试结果:101msecs
方法3测试结果:104msecs
方法4测试结果:2msecs

结果好像和预想的不一致,利用流和并行并没有比传统的外部迭代方式快。为什么会出现这种情况?其实iterator是不适合做 并行处理的,因为它的每一次迭代都要用前一次的结果,加上装箱和拆箱的损耗,反而更耗时。方法四利用了别的数据结构,代码的时间复杂度明显降了下来。看来要正确使用并行流也不是一件容易的事情,用不好的话,反而弄巧成拙,代码运行起来更慢。

如何正确使用并行流?

待更新.......

















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值