写在前面

我们都知道,在开发中有时候要想提高程序的效率,可以使用多线程去并行处理。而Java8的速度变快了,这个速度变快的原因中,很重要的一点就是Java8提供了并行方法,它使得我们的程序很容易就能切换成多线程,从而更好的利用CPU资源。

下面我们就来简单学习一下java8中得并行流与串行流。

并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。

Java8中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API 可以声明性地通过parallel()与sequential()在并行流与顺序(串行)流之间进行切换

Fork/Join框架

在说并行流之前呢,我们首先来来接一下这个Fork/Join框架框架。

Java 7开始引入了一种新的Fork/Join线程池,它可以执行一种特殊的任务:把一个大任务拆成多个小任务并行执行。即在必要的情况下,将一个大的任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行join汇总。

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

传统的线程池

我们就多线程来说吧,所谓的多线程就是把我们的任务分配到CPU不同的核上(也就是CPU不同的线程上)进行执行,我们以4核CPU为例。如果是传统线程的话,每个任务都有可能会阻塞,因为每个线程什么时候执行是由CPU时间片给他分配的执行权决定的,当这个时间片用完了以后,CPU会剥夺他的执行权,然后交给其他的线程去执行,这时就有可能出现阻塞的情况。即4核CPU我们可以看成4个线程,有可能其中俩线程中的一个任务阻塞造成后面的任务排队得不到执行,而另外两个没有阻塞的线程,则顺利执行完处于空闲状态了,这种有的线程在阻塞线程里的任务得不到执行,而别的不阻塞的线程空闲没有任务可以执行的状态,就造成了CPU资源的浪费,这样就会大大影响我们程序的执行效率。

Fork/Join框架

是把一个大任务拆分成若干个小任务,然后把这些小任务都压入到对应的线程中,也就是把这些小任务都压入到对应的CPU中(默认CPU有几核就有几个线程),然后形成一个个的线程队列。

Fork/Join任务的原理:判断一个任务是否足够小,如果是,直接计算,否则,就分拆成几个小任务分别计算。这个过程可以反复“裂变”成一系列小任务。

Fork/Join框架会将任务分发给线程池中的工作线程。Fork/Join框架的独特之处在于它使用“工作窃取”(work-stealing)算法。完成自己的工作而处于空闲的工作线程,能够从其他扔处于忙碌状态的工作线程中窃取等待执行的任务,每个工作线程都有自己的工作队列,这是使用双端队列(dequeue)来实现的。线程执行任务是从队列头部开始执行的,而处于空闲状态的线程,在窃取别的线程的任务的时候,是从被窃取线程的等待队列的队尾开始窃取的。这种情况下,就不会出现空闲的线程浪费CPU资源,因为一旦空闲便会去窃取任务执行。没有资源浪费,减少了线程的等待时间,所以效率就高,就提升了性能。

下面我们举个例子:如果要计算一个超大数组的和,最简单的做法是用一个循环在一个线程内完成。还有一种方法,可以把数组拆成两部分,分别计算,最后加起来就是最终结果,这样可以用两个线程并行执行,如果拆成两部分还是很大,我们还可以继续拆,用4个线程并行执行,这种即使用Fork/Join对大数据进行并行求和。

Fork/Join框架的使用

下面我们来写测试类演示一下:实现数的累加操作,比如说计算1到100亿的和。

我们要编写一个类继承RecursiveTask类,并重写compute()方法。

package com.cqq.java8.parallel;

import java.util.concurrent.RecursiveTask;

/**
* @Description:递归进行拆分
* @date 2021/3/14 7:55
*/
public class ForkJoinCalculate extends RecursiveTask<Long> { private Long start;
private Long end; public ForkJoinCalculate(Long start, Long end) {
this.start = start;
this.end = end;
} //临界值,当大于临界值的时候就一直拆分,小于临界值就不再进行拆分了
private static final long THREASHOLD = 100000000L; //重写compute方法
@Override
protected Long compute() { long length = end - start;
if(length <= THREASHOLD){//到临界值就不能再拆了
long sum = 0;
for (Long i = start; i <= end; i++) {
sum += i;
}
return sum;
}else{//不到临界值就进行拆分
long middle = (end + start);
ForkJoinCalculate left = new ForkJoinCalculate(start,middle);
//拆分子任务,同时压入线程队列
left.fork();
ForkJoinCalculate right = new ForkJoinCalculate(middle+1,end);
right.fork();
//拆完之后,合并,把fork()之后的结果得一个个合并,即累加总和
return left.join()+right.join(); }
}
}

测试方法

package com.cqq.java8;

import com.cqq.java8.parallel.ForkJoinCalculate;
import org.junit.Test; import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask; /**
* @Description:
* @date 2021/3/14 8:17
*/
public class TestForkJoin {
@Test
public void test01(){ //开始时间
Instant start = Instant.now();
//ForkJoin的执行需要一个ForkJoinPool的支持
ForkJoinPool pool = new ForkJoinPool(); ForkJoinTask<Long> task = new ForkJoinCalculate((long) 0,100000000L);
Long invoke = pool.invoke(task);
System.out.println(invoke);
//结束时间
Instant end = Instant.now();
//计算一下时间用 耗时多少
System.out.println(Duration.between(start,end).toMillis()); } //一个普通for循环即传统的单线程的测试类 与Fork/Join的执行结果做对比
@Test
public void test02(){ Instant start = Instant.now();
long sum = 0L; for (long i = 0; i < 100000000L; i++) {
sum += i;
}
System.out.println(sum);
Instant end = Instant.now();
System.out.println(Duration.between(start,end).toMillis()); }
}

测试结果

类加和 ForkJoin耗时 传统单线程耗时
1-1亿 521 85
1-10亿 241 363
1-100亿 1103 2431

从测试结果可以看出,当任务量不大时,传统单线程耗时短,任务达到一定量时ForkJoin的性能就很好了,因为在任务量不大时,拆分任务也要耗时,所以总的执行时间就比较长。说明,多线程也是要在合适的时候用才能提升性能。

Java8中的并行流

在Java 8中我们用的是parallel()方法,对并行流进行了优化。但是实际上底层还是用的Fork/Join框架。

    @Test
public void test03(){ Instant start = Instant.now(); //顺序流
long reduce = LongStream.rangeClosed(0, 100000000L)
.reduce(0, Long::sum); //使用parallel()并行流
OptionalLong reduce1 = LongStream.rangeClosed(0, 100000000L)
.parallel()//并行
.reduce(Long::sum); Instant end = Instant.now();
System.out.println(Duration.between(start,end).toMillis()); }

Java8 中不仅仅对代码进行了优化,而且效率也大大提升。

Java8的新特性--并行流与串行流的更多相关文章

  1. 【Java8新特性】关于并行流与串行流,你必须掌握这些!!

    写在前面 提到Java8,我们不得不说的就是Lambda表达式和Stream API.而在Java8中,对于并行流和串行流同样做了大量的优化.对于并行流和串行流的知识,也是在面试过程中,经常被问到的知 ...

  2. JDK8--07:并行流与串行流

    JDK8中,提供了并行流和串行流,使用parallel()和sequential()来处理,parallel()为并行流sequential()为串行流,两者可以相互转换,以最后一个为准 LongSt ...

  3. Java8新特性 并行流与串行流 Fork Join

    并行流就是把一个内容分成多个数据块,并用不同的线程分 别处理每个数据块的流. Java 8 中将并行进行了优化,我们可以很容易的对数据进行并 行操作. Stream API 可以声明性地通过 para ...

  4. Java8新特性 - 并行流与串行流

    并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流. Java8中将并行进行了优化,我们可以很容易的对数据进行并行操作.Stream API可以声明性地通过parallel()和 ...

  5. 三、并行流与串行流 Fork/Join框架

    一.并行流概念: 并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流. java8中将并行进行了优化,我们可以很容易的对数据进行并行操作.Stream API可以声明性的通过pa ...

  6. ForkJoin、并行流计算、串行流计算对比

    ForkJoin 什么是 ForkJoin ForkJoin 是一个把大任务拆分为多个小任务来分别计算的并行计算框架 ForkJoin 特点:工作窃取 这里面维护的都是双端队列,因此但其中一个线程完成 ...

  7. 深度分析:java8的新特性lambda和stream流,看完你学会了吗?

    1. lambda表达式 1.1 什么是lambda 以java为例,可以对一个java变量赋一个值,比如int a = 1,而对于一个方法,一块代码也是赋予给一个变量的,对于这块代码,或者说被赋给变 ...

  8. Java8 Stream新特性详解及实战

    Java8 Stream新特性详解及实战 背景介绍 在阅读Spring Boot源代码时,发现Java 8的新特性已经被广泛使用,如果再不学习Java8的新特性并灵活应用,你可能真的要out了.为此, ...

  9. java8的新特性以及用法简介

    1. 介绍 2 接口的默认方法 2 lambda表达式 2.1 函数式接口 2.2 方法与构造函数引用 2.3 访问局部变量 2.4 访问对象字段与静态变量 3. 内建函数式接口 3.1 Predic ...

随机推荐

  1. vue中子组件更新父组件

    当在子组件里更改了某些信息且关闭子组件后,需要父组件更新修改后的内容,该如何操作 1.$emit触发 父组件 @add="add(val)" 子组件 this.$emit('add ...

  2. 阿里巴巴java开发手册(2020版)

    阿里巴巴java开发手册(2020版) 2020版 链接: pan.baidu.com/s/1Zls_FUBK- 密码: titz 2019版 链接: pan.baidu.com/s/1cvCVQvj ...

  3. 使用SQL-Server分区表功能提高数据库的读写性能

    首先祝大家新年快乐,身体健康,万事如意. 一般来说一个系统最先出现瓶颈的点很可能是数据库.比如我们的生产系统并发量很高在跑一段时间后,数据库中某些表的数据量会越来越大.海量的数据会严重影响数据库的读写 ...

  4. hihoCoder Challenge 3

    #1065 : 全图传送 时间限制:30000ms 单点时限:3000ms 内存限制:256MB 描述 先知法里奥是 Dota 系列中的一个英雄.机动性强,推塔能力一流,打钱速度快,传送技能使先知可以 ...

  5. 免费在线 Linux Desktop 环境

    免费在线 Linux Desktop 环境 Run Linux OS Distributions online https://www.onworks.net/os-distributions 免费测 ...

  6. algorithm & bitwise operation & the best leetcode solutions

    algorithm & bitwise operation & the best leetcode solutions leetcode 136 single-number the b ...

  7. input number step

    input number step <!DOCTYPE html> <html> <body> <h1>The input step attribute ...

  8. vue v-io 父子组件双向绑定多个数据

    vue-io-directive 可以减少使用emit,组件自带的v-model好像也只能设置一个 安装 npm i vue-io-directive 使用 import Vue from 'vue' ...

  9. Flutter 使用Tabbar不要Title

    原文 Demo 1 import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp ext ...

  10. DENIEL SOIBIM:真正自律的人都在做这些事情!

    生活节奏的加快,使得很多人无法适从.很多人,浑浑噩噩,庸庸碌碌,觉得一天做了很多事,却总是一事无成.还有些人,觉得得过且过也很好,但是到头来,却让自己陷入慌乱之中.本想要自由自在的生活,但是却往往却被 ...