JDK1.8新特性总结 - 篇三(并行流与顺序流)

一. 并行流与串行流

          并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块中的流。JDK8中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream Api可以声明式的通过parallel()与sequential()在并行流和串行流(又被称作“顺序流”)之间进行切换。

二. Fork/Join框架

          Fork/Join框架就是在必要的情况下,将一个大任务拆分(fork)成若干个子任务(拆到不可再拆为止),再将一个个子任务就是并行运算,最终将值进行join汇总。

三. Fork/Join框架与传统线程池的区别

          传统的线程池虽然也能把任务进行拆分成若干子任务,借助不同线程来执行任务,但有一个问题:一旦某个子任务由于某些原因无法继续执行,那么负责执行该任务的线程将进入阻塞状态,影响了分配给该线程的其他后续任务的执行。

          在Fork/Join框架中情况则大不相同。如果线程A被某个子任务阻塞,A不会进入阻塞状态,而是会主动寻找尚未被执行的其它子任务(可以从自己的后续任务队列中找,也可以从其它线程的任务队列中偷)。这种模式被称作“工作窃取”模式(Working Stealing),当执行新的任务时,线程利用Fork/join框架可以将任务拆分成更小的任务,并将小任务分配/加入到线程队列中,如果自己空闲,就会从一个随机线程的任务队列中偷一个尚未被执行的任务来运行。

          其实早在jdk1.7中已经有Fork/Join的实现了,下面来看看在JDK1.7中如何使用:

/**
 * jdk1.7中使用Fork/Join框架
 *
 * 需要定义:
 * 1. 如何拆分
 * 2. 拆到什么程度为止
 */
public class ForkJoinCalculate extends RecursiveTask<Long> {
    private static final long serialVersionUID = 1212853982210556381L;

    private long start;
    private long end;
    //拆分的阈值,任务的最小单位是100000,拆到这个程度就不用再拆了
    private static final long THRESHOLD = 100000;

    public ForkJoinCalculate(long start, long end){
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        long length = start - end;
        if(length <= THRESHOLD){ //进行求和
            long sum  = 0;
            for(long i = start; i <= end; i++){
                sum += i;
            }
            return sum;
        }else{ //进行拆分
            long middle = (start + end) / 2;

            ForkJoinCalculate left = new ForkJoinCalculate(start, middle);
            left.fork(); //拆分子任务,并将任务压入线程队列

            ForkJoinCalculate right = new ForkJoinCalculate(middle + 1, end);
            right.fork();

            //结果汇总
            return left.join() + right.join();
        }
    }
}

       

/**
 * 使用Fork/join求和
 */
@org.junit.Test
public void test2(){
	Instant start = Instant.now();

	//需要ForkJoin线程池的支持
	ForkJoinPool pool = new ForkJoinPool();
	ForkJoinCalculate fork = new ForkJoinCalculate(0, 1000000000L);
	Long sum = pool.invoke(fork);
	System.out.println(sum);

	Instant end = Instant.now();

	System.out.println(Duration.between(start, end).toMillis());
}

              jdk1.8的写法如下:

/**
 * 使用jdk1.8求和
 */
public void test3(){
	Instant start = Instant.now();

	Long sum = LongStream.rangeClosed(0, 1000000000L)
			.parallel()
			.reduce(0, Long::sum);

	Instant end = Instant.now();

	System.out.println(Duration.between(start, end).toMillis());
}

             注意,由于Fork/Join会充分发挥CPU多核性能,因此电脑不好的朋友不要轻易运行上述代码,或者在测试时把计算的数据量调低。我的笔记本CPU型号i5-6300HQ ,直接死机了。

发布了45 篇原创文章 · 获赞 13 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/miaomiao19971215/article/details/90598768