本文源码:GitHub·点这里 || GitEE·点这里

一、Fork/Join框架

Java提供Fork/Join框架用于并行执行工作,外围的思维就是将一个大工作切分成多个小工作,而后汇总每个小工作的执行后果失去这个大工作的最终后果。

这种机制策略在分布式数据库中十分常见,数据分布在不同的数据库的正本中,在执行查问时,每个服务都要跑查问工作,最初在一个服务上做数据合并,或者提供一个两头引擎层,用来汇总数据:

外围流程:切分工作,模块工作异步执行,单任务后果合并;在编程外面,通用的代码不多,然而通用的思维却随处可见。

二、外围API和办法

1、编码案例

基于1+2..+100的计算案例演示Fork/Join框架根底用法。

import java.util.concurrent.ForkJoinPool;import java.util.concurrent.ForkJoinTask;import java.util.concurrent.RecursiveTask;public class ForkJoin01 {    public static void main (String[] args) {        int[] numArr = new int[100];        for (int i = 0; i < 100; i++) {            numArr[i] = i + 1;        }        ForkJoinPool pool = new ForkJoinPool();        ForkJoinTask<Integer> forkJoinTask =                pool.submit(new SumTask(numArr, 0, numArr.length));        System.out.println("合并计算结果: " + forkJoinTask.invoke());        pool.shutdown();    }}/** * 线程工作 */class SumTask extends RecursiveTask<Integer> {    /*     * 切分工作块的阈值     * 如果THRESHOLD=100     * 输入:main【求和:(0...100)=5050】 合并计算结果: 5050     */    private static final int THRESHOLD = 100;    private int arr[];    private int start;    private int over;    public SumTask(int[] arr, int start, int over) {        this.arr = arr;        this.start = start;        this.over = over;    }    // 求和计算    private Integer sumCalculate () {        Integer sum = 0;        for (int i = start; i < over; i++) {            sum += arr[i];        }        String task = "【求和:(" + start + "..." + over + ")=" + sum +"】";        System.out.println(Thread.currentThread().getName() + task);        return sum ;    }    @Override    protected Integer compute() {        if ((over - start) <= THRESHOLD) {            return sumCalculate();        }else {            int middle = (start + over) / 2;            SumTask left = new SumTask(arr, start, middle);            SumTask right = new SumTask(arr, middle, over);            left.fork();            right.fork();            return left.join() + right.join();        }    }}

2、外围API阐明

ForkJoinPool:线程池最大的特点就是分叉(fork)合并(join)模式,将一个大工作拆分成多个小工作,并行执行,再联合工作窃取算法进步整体的执行效率,充分利用CPU资源。

ForkJoinTask:运行在ForkJoinPool的一个工作形象,能够了解为类线程然而比线程轻量的实体,在ForkJoinPool中运行的大量ForkJoinWorkerThread能够持有大量的ForkJoinTask和它的子工作,同时也是一个轻量的Future,应用时应防止较长阻塞或IO。

继承子类:

  • RecursiveAction:递归无返回值的ForkJoinTask子类;
  • RecursiveTask:递归有返回值的ForkJoinTask子类;

外围办法:

  • fork():在以后线程运行的线程池中创立一个子工作;
  • join():模块子工作实现的时候返回工作后果;
  • invoke():执行工作,也能够实时期待最终执行后果;

3、外围策略阐明

工作拆分

ForkJoinPool基于分治算法,将大工作一直拆分上来,每个子工作再拆分一半,直到达到最阈值设定的工作粒度为止,并且把工作放到不同的队列外面,而后从最底层的工作开始执行计算,并且往上一层合并后果,这样用绝对少的线程解决大量的工作。

工作窃取算法

大工作被宰割为独立的子工作,并且子工作别离放到不同的队列里,并为每个队列创立一个线程来执行队列里的工作,假如线程A优先把调配到本人队列里的工作执行结束,此时如果线程E对应的队列里还有工作期待执行,闲暇的线程A会窃取线程E队列里工作执行,并且为了缩小窃取工作时线程A和被窃取工作线程E之间的产生竞争,窃取工作的线程A会从队列的尾部获取工作执行,被窃取工作线程E会从队列的头部获取工作执行。

工作窃取算法的长处:线程间的竞争很少,充分利用线程进行并行计算,然而在工作队列里只有一个工作时,也可能会存在竞争状况。

三、利用案例剖析

在后端系统的业务开发中,可用做权限校验,批量定时工作状态刷新等各种性能场景:

如上图,假如数据的主键id分段如下,数据场景可能是数据源的连贯信息,或者产品有效期相似业务,都能够基于线程池工作解决:

权限校验

基于数据源的连贯信息,判断数据源是否可用,例如:判断连贯是否可用,用户是否有库表的读写权限,在数据源多的状况下,基于线程池疾速校验。

状态刷新

在定时工作中,常常见到状态类的刷新操作,例如判断产品是否在有效期范畴内,在有效期范畴之外,把数据置为生效状态,都能够利用线程池疾速解决。

四、源代码地址

GitHub·地址https://github.com/cicadasmile/java-base-parentGitEE·地址https://gitee.com/cicadasmile/java-base-parent

举荐浏览:Java并发系列

序号文章题目
01Java并发:线程的创立形式,状态周期治理
02Java并发:线程外围机制,根底概念扩大
03Java并发:多线程并发拜访,同步控制
04Java并发:线程间通信,期待/告诉机制
05Java并发:乐观锁和乐观锁机制
06Java并发:Lock机制下API用法详解