Java8实战之(六)并行数据处理与性能

在这里插入图片描述
在这里插入图片描述

6.1 并行流

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.1.1 将顺序流转换成并行流

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.1.2 测试流性能

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package com.java.lamdba.six;

import java.util.function.Function;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;

public class Demo {
    public static 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("Result: " + sum);
            if (duration < fastest){
                fastest = duration;
            }
        }
        return fastest;
    }
    public static void main(String[] args) {
        // 累加数据
        long sum = IntStream.iterate(1, i -> i+1).limit(10).sum();
//        long sum1 = Stream.iterate(1L, i -> i+1).limit(10).parallel().reduce(0L,Long::sum);
//        System.out.println(sum);

        // 并行流性能比较
        System.out.println("Sequential sum done in:" +
                measureSumPerf(Demo::parallelrangedSum, 10_000_000) + " msecs");
    }

    /** 顺序流累加
     * @param n
     * @return
     */
    public static long sequentialSum(long n) {
        return Stream.iterate(1L, i -> i + 1)
                .limit(n)
                .reduce(0L, Long::sum);
    }

    /** 并行流累加
     * @param n
     * @return
     */
    public static long parallelSum(long n) {
        return Stream.iterate(1L, i -> i + 1)
                .limit(n)
                .parallel()
                .reduce(0L, Long::sum);
    }

    /** 并行流累加(避免拆箱)
     * @param n
     * @return
     */
    public static long parallelrangedSum(long n) {
        return LongStream.rangeClosed(1L, n)
                .parallel()
                .reduce(0L, Long::sum);
    }


    /** 避免拆箱的操作
     * @param n
     * @return
     */
    public static long rangedSum(long n) {
        return LongStream.rangeClosed(1, n)
                .reduce(0L, Long::sum);
    }

    /** 迭代器累加
     * @param n
     * @return
     */
    public static Long iterativeSum(long n) {
        long result = 0;
        for (long i = 1L; i <= n; i++) {
            result += i;
        }
        return result;
    }
}

6.1.3 正确使用并行流

在这里插入图片描述


    /** 演示错误的并行流
     * @param n 
     * @return
     */
    public static long sideEffectSum(long n) {
        Accumulator accumulator = new Accumulator();
        LongStream.rangeClosed(1, n).parallel().forEach(accumulator::add);
        return accumulator.total;
    }

在这里插入图片描述
在这里插入图片描述

6.1.4 高效使用并行流

在这里插入图片描述
重点
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.2 分支/合并框架

在这里插入图片描述

6.2.1 使用RecursiveTask

要想把任务提交到池,必须创建RecursiveTask的一个子类,其中R是并行化任务(或者子任务)产生的结果类型。或者如果任务不返回结果,则没有泛型,并且要实现实现抽象方法compute
这个方法同时定义了将任务拆成子任务的逻辑,以及无法拆分时,单个任务的逻辑
在这里插入图片描述
在这里插入图片描述
这里有点像分治算法,下面我们以一个数字范围long数组表示求和.

package com.java.lamdba.six;

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.stream.LongStream;

public class ForkJoinSumCalculator extends RecursiveTask<Long> {

    private long[] numbers;

    // 子任务的起始位置和终止位置
    private int start;
    private int end;

    /**
     * 不再分割的数组大小
     */
    private final long THRESHOLD = 10_000;
    @Override
    protected Long compute() {
        int length = end - start;
        // 如果大小小于等于阈值,顺序执行
        if (length <= THRESHOLD) {
            return computeSequentially();
        }

        // 创建一个子任务为数组的前一半求和
        ForkJoinSumCalculator leftTask =
                new ForkJoinSumCalculator(numbers, start, start + length/2);
        // 利用ForkJoinTask线程异步执行新创建的子任务
        leftTask.fork();

        // 创建一个子任务为数组的后一半求和
        ForkJoinSumCalculator rightTask =
                new ForkJoinSumCalculator(numbers, start + length/2, end);

        // 同步执行第二个子任务,有可能允许进一步递归划分
        Long rightResult = rightTask.compute();

        // 读取第一个子任务的结果,如果尚未完成就等待
        Long leftResult = leftTask.join();

        // 拼接来两个结果
        return leftResult + rightResult;
    }

    private long computeSequentially() {
        long sum = 0;
        for (int i = start; i < end; i++) {
                sum += numbers[i];
        }
        return sum;
    }

    public ForkJoinSumCalculator(long[] numbers) {
        this(numbers, 0, numbers.length);
    }
    private ForkJoinSumCalculator(long[] numbers, int start, int end) {
        this.numbers = numbers;
        this.start = start;
        this.end = end;
    }

    public static long forkJoinSum(long n) {
        long[] numbers = LongStream.rangeClosed(1, n).toArray();
        ForkJoinTask<Long> task = new ForkJoinSumCalculator(numbers);
        return new ForkJoinPool().invoke(task);
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.2.2 使用分支/合并框架最佳做法

在这里插入图片描述

6.2.3 工作窃取

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.3 Spliterator

在这里插入图片描述
在这里插入图片描述

6.3.1 拆分过程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.3.2 实现你自己的Spliterator

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package com.java.lamdba.six;

import lombok.Data;

import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * 计算句子单词的个数
 */
@Data
public class WordCounter {
    public WordCounter() {
    }

    public static void main(String[] args) {
        final String SENTENCE =
                " Nel mezzo del cammin di nostra vita " +
                        "mi ritrovai in una selva oscura" +
                        " ché la dritta via era smarrita ";
        Stream<Character> stream = IntStream.range(0, SENTENCE.length())
                .mapToObj(SENTENCE::charAt);
        System.out.println("Found " + new WordCounter().countWords(stream) + " words");
    }

    private int count;

    private boolean lastSpace;

    public static int countWordsIteratively(String s) {
        int counter = 0;
        boolean lastSpace = true;
        for (char c : s.toCharArray()) {
            // 如果是空格,先将空格状态打开
            if (Character.isWhitespace(c)) {
                lastSpace = true;
            } else {
                if (lastSpace) {
                    counter++;
                }
                lastSpace = false;
            }
        }
        return counter;
    }
    public WordCounter(int count, boolean lastSpace) {
        this.count = count;
        this.lastSpace = lastSpace;
    }

    /** 和countWordsIteratively 逻辑一样
     * @param character
     * @return
     */
    public WordCounter accmulate(Character character){
        if (Character.isWhitespace(character)){
            return lastSpace ? this:new WordCounter(count,true);
        }else {

            return lastSpace ? new WordCounter(count + 1,false) : this;
        }
    }

    // 合并两个WordCounter 把其累加起来 仅需要合并总和,无须关系空格状态
    public WordCounter combine(WordCounter wordCounter) {
        return new WordCounter(count + wordCounter.count,
                wordCounter.lastSpace);
    }

    private int countWords(Stream<Character> stream) {
        // 使用流接收数据
        WordCounter wordCounter = stream.reduce(new WordCounter(0, true),
                WordCounter::accmulate,
                WordCounter::combine);
        return wordCounter.getCount();
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package com.java.lamdba.six;

import java.util.Spliterator;
import java.util.function.Consumer;

class WordCounterSpliterator implements Spliterator<Character> {
    private final String string;
    private int currentChar = 0;
    public WordCounterSpliterator(String string) {
        this.string = string;
    }
    // 处理当当前字符,如果还有字符要返回的就返回true
    @Override
    public boolean tryAdvance(Consumer<? super Character> action) {
        action.accept(string.charAt(currentChar++));
        return currentChar < string.length();
    }
    @Override
    public Spliterator<Character> trySplit() {
        int currentSize = string.length() - currentChar;
        // 返回null说明拆分的已经足够小,可以顺序处理
        if (currentSize < 10) {
            return null;
        }
        // 将试探拆分的位置设定为要解析的String的中间
        for (int splitPos = currentSize / 2 + currentChar; splitPos < string.length(); splitPos++) {
            // 拆分直到下一个空格
            if (Character.isWhitespace(string.charAt(splitPos))) {

                // 创建一个新的WordCounterSpliterator来解析String从开始到拆分位置的部分
                Spliterator<Character> spliterator =
                        new WordCounterSpliterator(string.substring(currentChar,
                                splitPos));
                // 将这个WordCounterSpliterator的起始位置设为拆分位置
                currentChar = splitPos;
                return spliterator;
            }
        }
        return null;
    }
    @Override
    public long estimateSize() {
        return string.length() - currentChar;
    }
    @Override
    public int characteristics() {
        return ORDERED + SIZED + SUBSIZED + NONNULL + IMMUTABLE;
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.4 小结

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值