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是不适合做 并行处理的,因为它的每一次迭代都要用前一次的结果,加上装箱和拆箱的损耗,反而更耗时。方法四利用了别的数据结构,代码的时间复杂度明显降了下来。看来要正确使用并行流也不是一件容易的事情,用不好的话,反而弄巧成拙,代码运行起来更慢。
如何正确使用并行流?
待更新.......