本文源码: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-parent
GitEE·地址
https://gitee.com/cicadasmile/java-base-parent

推荐阅读:Java并发系列

序号 文章标题
01 Java并发:线程的创建方式,状态周期管理
02 Java并发:线程核心机制,基础概念扩展
03 Java并发:多线程并发访问,同步控制
04 Java并发:线程间通信,等待/通知机制
05 Java并发:悲观锁和乐观锁机制
06 Java并发:Lock机制下API用法详解

Java并发编程(07):Fork/Join框架机制详解的更多相关文章

  1. Java并发编程:线程封闭和ThreadLocal详解

    转载请标明出处: http://blog.csdn.net/forezp/article/details/77620769 本文出自方志朋的博客 什么是线程封闭 当访问共享变量时,往往需要加锁来保证数 ...

  2. Java并发编程3-抽象同步队列AQS详解

    AQS是AtractQueuedSynchronizer(队列同步器)的简写,是用来构建锁或其他同步组件的基础框架.主要通过一个int类型的state来表示同步状态,内部有一个FIFO的同步队列来实现 ...

  3. 【java并发系列】Fork/Join任务(转)

    原文链接 当我们需要执行大量的小任务时,有经验的Java开发人员都会采用线程池来高效执行这些小任务.然而,有一种任务,例如,对超过1000万个元素的数组进行排序,这种任务本身可以并发执行,但如何拆解成 ...

  4. Java并发编程实战 05等待-通知机制和活跃性问题

    Java并发编程系列 Java并发编程实战 01并发编程的Bug源头 Java并发编程实战 02Java如何解决可见性和有序性问题 Java并发编程实战 03互斥锁 解决原子性问题 Java并发编程实 ...

  5. 多线程高并发编程(8) -- Fork/Join源码分析

    一.概念 Fork/Join就是将一个大任务分解(fork)成许多个独立的小任务,然后多线程并行去处理这些小任务,每个小任务处理完得到结果再进行合并(join)得到最终的结果. 流程:任务继承Recu ...

  6. Java并发编程(02):线程核心机制,基础概念扩展

    本文源码:GitHub·点这里 || GitEE·点这里 一.线程基本机制 1.概念描述 并发编程的特点是:可以将程序划分为多个分离且独立运行的任务,通过线程来驱动这些独立的任务执行,从而提升整体的效 ...

  7. Java 并发编程中的 Executor 框架与线程池

    Java 5 开始引入 Conccurent 软件包,提供完备的并发能力,对线程池有了更好的支持.其中,Executor 框架是最值得称道的. Executor框架是指java 5中引入的一系列并发库 ...

  8. JAVA并发工具类---------------(Fork/Join)

    Fork/Join 分而治之 将一个大任务分成数个小任务执行,然后将这些小人物执行后的结果进行join汇总: (假设:你要计算1到1000的总和,你可以把它分成1-100,101-200,...... ...

  9. Java并发编程核心方法与框架-CountDownLatch的使用

    Java多线程编程中经常会碰到这样一种场景:某个线程需要等待一个或多个线程操作结束(或达到某种状态)才开始执行.比如裁判员需要等待运动员准备好后才发送开始指令,运动员要等裁判员发送开始指令后才开始比赛 ...

随机推荐

  1. windows如何解决Error: Registry key 'Software\JavaSoft\Java Runtime Environment'\CurrentVersion'

    问题再现与解读 首次,使用 java 命令+任意参数 都会报这个错误,最典型的就是java -version 解读一下这个报错信息,注册表路径 Software\JavaSoft\Java Runti ...

  2. Docker 入门教程(4)——docker-compse 服务编排

    Docker compose 简介 compose是用来定义和运行多个Docker容器. 比如一个简单的web项目,除了web服务之外,我们可能要需要数据库容器.注册中心容器等等.那我们需要: 定义各 ...

  3. django-rest-framework-源码解析003-视图家族和路由(APIView/GenericAPIView/mixins/generics/viewsets)

    视图家族 视图家族在rest_framework源码位置和学习曲线为: rest_framework.views: 基本视图(APIView) rest_framework.generics: 工具视 ...

  4. 数据库(二):初识sql语句

    进击のpython ***** 数据库--初识sql语句 前面提到了说,数据库管理系统就像我们曾经做过的输入命令返回结果的socket通信差不多 那既然提到了命令,在MySQL中,有一些基本的语句,就 ...

  5. 大数据框架Hive优化方法

    常规调优手段 Fetch抓取 某些情况查询不必用MapReduce计算,比如select*,可以直接读取文件 本地模式 有时数据量比较小,hive可以通过本地模式在单台机器上处理所有任务,对于小数据集 ...

  6. Centos 7 下安装PHP7.2(与Apache搭配的安装方式)

    (1)源码包下载 百度云下载地址:https://pan.baidu.com/s/1xH7aiGYaX62wij4ul5P-ZQ 提取码:m9zc (2)安装php依赖组件: yum -y insta ...

  7. springboot 基于JS-SDK实现微信分享(一)

    本文主要介绍了SpringBoot 基于JS-SDK实现自定义微信分享,并通过本地测试的方式进行调试,文中通过微信实现分享流程及示例代码进行非常详细的介绍,希望本文对开发爱好者学习或者工作具有一定的参 ...

  8. 如何使用Excel管理项目?

    1.什么是复杂问题? 复杂问题需要很多道工序,涉及到与多个人进行沟通,人的注意力没法持续关注,导致很容易忘掉很多重要步骤.像这种问题就要用到项目管理工具,在重要的节点上,来检查自己是否遗漏了重要的环节 ...

  9. PHP stat() 函数

    定义和用法 stat() 函数返回关于文件的信息. 该函数将返回一个包含下列元素的数组: [0] 或 [dev] - 设备编号 [1] 或 [ino] - inode 编号 [2] 或 [mode] ...

  10. PHP sscanf() 函数

    实例 Parse a string: <?php高佣联盟 www.cgewang.com$str = "age:30 weight:60kg";sscanf($str,&qu ...