一.并行流
1.并行流运行时:内部使用了fork-join框架
其默认线程数为处理器数量,Runtime.getRuntime().availableProcessors()TreeSet 好
接下来看下性能测试例子:
public class StreamDemo7 {
//并行方式1
public long Parsum1(long n) {
Long sum= Stream.iterate(1L, i->i+1)
.limit(n)
.parallel()
.reduce(0L,Long::sum);
return sum;
}
//并行方式2
public long Parsum2(long n) {
//在数量大的时候,有初始值比没初始值速度更快
Long sum= LongStream.rangeClosed(0, n)
.parallel()
.reduce(0L, Long::sum);
return sum;
//这里没有拆装箱
}
//并行方式3
public long Parsum3(long n) {
Long sum= LongStream.rangeClosed(0, n)
.parallel()
.reduce(0L,(a,b)->a+b);
return sum;
}
//窜行
public long cuanSum(long n) {
Long sum= LongStream.rangeClosed(0, n)
.reduce(0L, Long::sum);
return sum;
}
//for
public long sumFor(long n) {
long sum=0L;
for (long i = 1; i <= n; i++) {
sum+=i;
}
return sum;
}
//传统线程
public long callSum(long n) throws InterruptedException, ExecutionException {
ExecutorService es= Executors.newFixedThreadPool(8);
long avg=n/8;
long num2=avg*2;
long num3=avg*3;
long num4=avg*4;
long num5=avg*5;
long num6=avg*6;
long num7=avg*7;
//运行线程,返回结果
Future fu1=es.submit(new MyCallable(1, avg));
Future fu2=es.submit(new MyCallable(avg+1, num2));
Future fu3=es.submit(new MyCallable(num2+1, num3));
Future fu4=es.submit(new MyCallable(num3+1, num4));
Future fu5=es.submit(new MyCallable(num4+1, num5));
Future fu6=es.submit(new MyCallable(num5+1, num6));
Future fu7=es.submit(new MyCallable(num6+1, num7));
Future fu8=es.submit(new MyCallable(num7+1, n));
//这个不需要
// while(true){
// if (fu1.isDone()&&fu2.isDone()&&fu3.isDone()&&fu4.isDone()&&fu5.isDone()) {
// System.out.println(fu1.get()+fu2.get()+fu3.get()+fu4.get()+fu5.get());
// break;
// }
// }
//get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
long sum=fu1.get()+fu2.get()+fu3.get()+fu4.get()+fu5.get()+fu6.get()+fu7.get()+fu8.get();
es.shutdown();
return sum;
}
@Test
public void testName() throws InterruptedException, ExecutionException {
long start=0L;
long duration=0L;
long sum=0L;
long temp=Long.MAX_VALUE;
//
// for (int i = 0; i < 2; i++) {
start=System.currentTimeMillis();
sum=sumFor(10_0000_0000);
duration=System.currentTimeMillis()-start;
if (duration{
private long sum1;
private long sum2;
public MyCallable(long sum1,long sum2) {
this.sum1=sum1;
this.sum2=sum2;
}
@Override
public Long call() throws Exception {
long sum=0;//定义在里面更好一些
for (long i = sum1; i < sum2+1; i++) {
sum+=i;
}
//返回计算结果
return sum;
}
}
既然并行流使用了frok-join,肯定有一种自动机制来为你拆分流。这种新的自动机制称为Spliterator
Spliterator是Java 8中加入的另一个新接口;这个名字代表“可分迭代器”(splitableiterator)。
二.关于fork-join框架
1.流程图如下:
当把任务传给ForkJoinPool时,这个任务就由池中的一个线程
执行,这个线程会调用任务的compute方法。该方法会检查任务是否小到足以顺序执行,如果不
够小则会把要求和的数组分成两半,分给两个新的ForkJoinSumCalculator,而它们也由
ForkJoinPool安排执行。因此,这一过程可以递归重复,把原任务分为更小的任务,直到满足
不方便或不可能再进一步拆分的条件(本例中是求和的项目数小于等于10000)。这时会顺序计
算每个任务的结果,然后由分支过程创建的(隐含的)任务二叉树遍历回到它的根。接下来会合
并每个子任务的部分结果,从而得到总任务的结果。这一过程如图
原理:
分支/合并框架工程用一种称为工作窃取(work stealing)的技术来解决这个问题。在实际应
用中,这意味着这些任务差不多被平均分配到ForkJoinPool中的所有线程上。每个线程都为分
配给它的任务保存一个双向链式队列,每完成一个任务,就会从队列头上取出下一个任务开始执
行。基于前面所述的原因,某个线程可能早早完成了分配给它的所有任务,也就是它的队列已经
空了,而其他的线程还很忙。这时,这个线程并没有闲下来,而是随机选了一个别的线程,从队
列的尾巴上“偷走”一个任务。这个过程一直继续下去,直到所有的任务都执行完毕,所有的队
列都清空。这就是为什么要划成许多小任务而不是少数几个大任务,这有助于更好地在工作线程
之间平衡负载。
一般来说,这种工作窃取算法用于在池中的工作线程之间重新分配和平衡任务。下图展示
了这个过程。当工作线程队列中有一个任务被分成两个子任务时,一个子任务就被闲置的工作线
程“偷走”了。如前所述,这个过程可以不断递归,直到规定子任务应顺序执行的条件为真
public class FrokJoinDemo extends RecursiveTask{
private static final long serialVersionUID = 1L;
//需求自已分组和合并
private long start=0;
private long end=0;
private static final long THRESHOLD=1_0000L;
public FrokJoinDemo(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
long sum = 0;
if (end-start<=THRESHOLD) {
for (long i = start; i <= end; i++) {
sum+=i;
}
}else{
//进行细分
long middle=(start+end)/2;
FrokJoinDemo left = new FrokJoinDemo(start, middle);
FrokJoinDemo right = new FrokJoinDemo(middle+1, end);
left.fork();
right.fork();
sum=left.join()+right.join();
}
return sum;
}
public void test1() throws InterruptedException, ExecutionException {
Instant start=Instant.now();
//线程池
ForkJoinPool forkJoinPool=new ForkJoinPool();
FrokJoinDemo demo=new FrokJoinDemo(0, 100_0000_0000L);
//有返回值的
//方式1
Long invoke = forkJoinPool.invoke(demo);
System.out.println("结果"+invoke);
//方式2
// public abstract class ForkJoinTask implements Future, Serializable {}
// ForkJoinTask实现了Future接口
/*ForkJoinTask submit = forkJoinPool.submit(demo);
//这个需要抛异常
Long sum =submit.get();
System.out.println("结果"+sum);*/
Instant end=Instant.now();
System.out.println("毫秒"+Duration.between(start, end).toMillis());
}