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