java.util.concurrent
public class ForkJoinPool extends AbstractExecutorService
public abstract class ForkJoinTask<V> implements Future<V>, Serializable
public class ForkJoinWorkerThread extends Thread

Fork/Join

Fork/Join技术是分治算法(Divide-and-Conquer)的并行实现,它是一项可以获得良好的并行性能的简单且高效的设计技术。

ForkJoinPool的优势在于,可以充分利用多cpu,多核cpu的优势,把一个任务拆分成多个“小任务”,
把多个“小任务”放到多个处理器核心上并行执行;当多个“小任务”执行完成之后,再将这些执行结果合并起来即可。 JDK用来执行Fork/Join任务的工作线程池默认大小等于CPU核心数。在一个4核CPU上,最多可以同时执行4个子任务。 目的是为了帮助我们更好地利用多处理器带来的好处,使用所有可用的运算能力来提升应用的性能。 1. Fork/Join框架主要由ForkJoinPool、ForkJoinWorkerThread和ForkJoinTask来实现 2. ForkJoinPool中只可以运行ForkJoinTask类型的任务
(在实际使用中,也可以接收Runnable/Callable任务,但在真正运行时,也会把这些任务封装成ForkJoinTask类型的任务) 3. ForkJoinTask表示一个任务,
ForkJoinTask的子类中有RecursiveAction和RecursiveTask
RecursiveAction无返回结果,RecursiveTask有返回结果
工作中,一般重写RecursiveAction或RecursiveTask的compute(),完成计算或者可以进行任务拆分。 4. 调用ForkJoinTask的fork()的方法,可以让其他空闲的线程执行这个ForkJoinTask
调用ForkJoinTask的join()的方法,将多个小任务的结果进行汇总。 5. ForkJoinWorkerThread是运行ForkJoinTask任务的工作线程

work-stealing(工作窃取)算法

ForkJoinPool中,线程池中每个工作线程(ForkJoinWorkerThread)都对应一个任务队列(WorkQueue),

工作线程优先处理来自自身队列的任务(LIFO),然后以FIFO的顺序随机窃取其他队列中的任务。

ForkJoinPool

ForkJoinPool并行的实现了分治算法(Divide-and-Conquer):把任务递归的拆分为各个子任务,这样可以更好的利用系统资源

ForkJoinPool中的任务分为两种:

  1. 一种是本地提交的任务(Submission task,如 execute、submit 提交的任务)
  2. 另外一种是 fork 出的子任务(Worker task)

提交任务

外部任务(external/submissions task)提交三种方式:

    execute()是直接向池提交一个任务来异步执行,无返回结果;

    invoke()会一直阻塞到任务执行完成返回计算结果

    submit()也是异步执行,但是会返回提交的任务,在适当的时候可通过task.get()获取执行结果,实现同步到主线程

子任务(Worker task)提交:
由任务的fork()方法完成。
任务被分割(fork)之后调用了ForkJoinPool.WorkQueue.push()方法直接把任务放到队列中等待被执行。 public static void invokeAll(ForkJoinTask<?> t1, ForkJoinTask<?> t2) {
int s1, s2;
t2.fork();
if (((s1 = t1.doInvoke()) & ABNORMAL) != 0)
t1.reportException(s1);
if (((s2 = t2.doJoin()) & ABNORMAL) != 0)
t2.reportException(s2);
}

示例

对于fork/join模式,假如pool里面线程数量是固定的,那么调用子任务的fork方法相当于A先分工给B和C,
然后A当监工不干活,B和C去完成A交代的任务。
所以上面的模式相当于浪费了一个线程。 如果使用invokeAll相当于A分工给B和C后,A和B和C都去完成工作。这样缩短了执行的时间。 /**
* 测试 ForkJoinPool 线程池的使用
*/
public class task { /**
* 测试使用 ForkJoinPool 无返回值的任务执行
*/
public static void noResultTask() throws Exception {
ForkJoinPool pool = new ForkJoinPool();
pool.submit(new PrintTask(1, 200));
pool.shutdown();
}
} /**
* 无返回值的打印任务
*/
class PrintTask extends RecursiveAction { private static final long serialVersionUID = 1L;
private static final int THRESHOLD = 50;
private int start;
private int end; public PrintTask(int start, int end) {
this.start = start;
this.end = end;
} @Override
protected void compute() {
//当 结束值 比 起始值 大于 50 时,按数值区间平均拆分为两个任务;否则直接打印该区间的值
if (end - start < THRESHOLD) {
for (int i = start; i <= end; i++) {
System.out.println(Thread.currentThread().getName() + ", i = " + i);
}
} else {
int middle = (start + end) / 2;
//分成两个子任务
PrintTask firstTask = new PrintTask(start, middle);
PrintTask secondTask = new PrintTask(middle + 1, end);
//任务拆分
//firstTask.fork();
//secondTask.fork();
invokeAll(firstTask,secondTask);
}
}
} public class task { /**
* 测试使用 ForkJoinPool 有返回值的任务执行,对结果进行合并。计算 1 到 200 的累加和
*/
public static void hasResultTask() throws Exception {
//线程池
ForkJoinPool pool = new ForkJoinPool();
//提交任务
ForkJoinTask<Integer> task = pool.submit(new CalculateTask(1, 200));
//得到结果
int result = task.get();
//关闭线程
pool.shutdown();
} } /**
* 有返回值的计算任务
*/
class CalculateTask extends RecursiveTask<Integer> { private static final long serialVersionUID = 1L;
private static final int THRESHOLD = 50;
private int start;
private int end; public CalculateTask(int start, int end) {
super();
this.start = start;
this.end = end;
} @Override
protected Integer compute() {
//当 结束值 比 起始值 大于 50 时,按数值区间平均拆分为两个任务,进行两个任务的累加值汇总
//否则直接计算累加值
if (end - start <= THRESHOLD) {
int result = 0;
for (int i = start; i <= end; i++) {
result += i;
}
return result;
} else {
int middle = (start + end) / 2;
CalculateTask firstTask = new CalculateTask(start, middle);
CalculateTask secondTask = new CalculateTask(middle + 1, end);
//任务拆分
invokeAll(firstTask,secondTask);
//任务合并
return firstTask.join() + secondTask.join();
}
} }

线程池 一 ForkJoinPool的更多相关文章

  1. 0041 Java学习笔记-多线程-线程池、ForkJoinPool、ThreadLocal

    什么是线程池 创建线程,因为涉及到跟操作系统交互,比较耗费资源.如果要创建大量的线程,而每个线程的生存期又很短,这时候就应该使用线程池了,就像数据库的连接池一样,预先开启一定数量的线程,有任务了就将任 ...

  2. 【转】线程及同步的性能 - 线程池 / ThreadPoolExecutors / ForkJoinPool

    线程池和ThreadPoolExecutors 虽然在程序中可以直接使用Thread类型来进行线程操作,但是更多的情况是使用线程池,尤其是在Java EE应用服务器中,一般会使用若干个线程池来处理来自 ...

  3. 线程及同步的性能 – 线程池/ ThreadPoolExecutors/ ForkJoinPool

    线程池和ThreadPoolExecutors 虽然在程序中可以直接使用Thread类型来进行线程操作,但是更多的情况是使用线程池,尤其是在Java EE应用服务器中,一般会使用若干个线程池来处理来自 ...

  4. [Java Performance] 线程及同步的性能之线程池/ThreadPoolExecutors/ForkJoinPool

    线程池和ThreadPoolExecutors   虽然在程序中可以直接使用Thread类型来进行线程操作,但是更多的情况是使用线程池,尤其是在Java EE应用服务器中,一般会使用若干个线程池来处理 ...

  5. (四)juc线程高级特性——线程池 / 线程调度 / ForkJoinPool

    13. 线程池 第四种获取线程的方法:线程池,一个 ExecutorService,它使用可能的几个池线程之一执行每个提交的任务,通常使用 Executors 工厂方法配置. 线程池可以解决两个不同问 ...

  6. Java并发包线程池之ForkJoinPool即ForkJoin框架(二)

    前言 前面介绍了ForkJoinPool相关的两个类ForkJoinTask.ForkJoinWorkerThread,现在开始了解ForkJoinPool.ForkJoinPool也是实现了Exec ...

  7. Java并发包线程池之ForkJoinPool即ForkJoin框架(一)

    前言 这是Java并发包提供的最后一个线程池实现,也是最复杂的一个线程池.针对这一部分的代码太复杂,由于目前理解有限,只做简单介绍.通常大家说的Fork/Join框架其实就是指由ForkJoinPoo ...

  8. [原创] JAVA 递归线程池测试 ExecutorService / ForkJoinPool

    测试工具使用递归的方式获取子进程的Msg消息,目前有2种常用的ExecutorService / ForkJoinPool 为了测试哪种效果较好,我们来写个测试Demo,循环5555555次+1(加锁 ...

  9. JAVA 递归线程池测试 ExecutorService / ForkJoinPool

    测试工具使用递归的方式获取子进程的Msg消息,目前有2种常用的ExecutorService / ForkJoinPool 为了测试哪种效果较好,我们来写个测试Demo,循环5555555次+1(加锁 ...

随机推荐

  1. Qt 【遍历文件夹文件,为listwidgetItem设置图标】

    效果图如上,通过qt本身的api去获取文件夹中文件的信息并且将其显示到listwidget上.只遍历当前文件夹的文件, 若文件夹中的子文件夹含有符合筛选器的文件那么并不会一同筛选出来 参考百度:htt ...

  2. 基于Docker构建Jenkins CI平台

    1.部署gitlab 1.1 部署gitlab docker run -d \ --name gitlab \ -p 8443:443 \ -p 9999:80 \ -p 9998:22 \ -v $ ...

  3. Dubbo---zookeeper 注册中心---xml配置

    1.项目结构(maven项目) 2.pom <?xml version="1.0" encoding="UTF-8"?> <project x ...

  4. Anaconda配置

    0x00 下载 为了更快的下载,可以到清华开源软件镜像站下载 地址:https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 在此以Anaconda ...

  5. Chrome 调试跨域问题解决方案之插件篇

    跨域,就是A域名下的js,想请求B域名下的接口数据.跨域,只存在于浏览器端.App和小程序不存在跨域问题.跨域,分浏览器策略和服务器策略. 如果服务器配置了允许跨域,那就没有跨域问题 如果uni-ap ...

  6. (转)OpenFire源码学习之十一:连接管理(下)

    转:http://blog.csdn.net/huwenfeng_2011/article/details/43416523 下面是下部分 C2S 1.当有客户端进行连接时根据Mina框架的模式首先调 ...

  7. HTML-参考手册: HTTP 方法:GET 对比 POST

    ylbtech-HTML-参考手册: HTTP 方法:GET 对比 POST 1.返回顶部 1. HTTP 方法:GET 对比 POST 两种最常用的 HTTP 方法是:GET 和 POST. 什么是 ...

  8. Dubbo入门到精通学习笔记(二):Dubbo管理控制台、使用Maven构建Dubbo的jar包、在Linux上部署Dubbo privider服务(shell脚本)、部署consumer服务

    文章目录 Dubbo管理控制台 1.Dubbo管理控制台的主要作用: 2.管理控制台主要包含: 3.管理控制台版本: 安装 Dubbo 管理控制台 使用Maven构建Dubbo服务的可执行jar包 D ...

  9. on() 不支持hover事件

    因为 .hover() 是 jQuery 自己定义的事件… 是为了方便用户绑定调用 mouseenter 和 mouseleave 事件而已,它并非一个真正的事件,所以当然不能当做 .bind() 中 ...

  10. 9. DMA

    9.1 介绍 Direct memory access(DMA) 直接存储器访问. 这两个DMA控制器总共有16个流(每个控制器8个),每个流用于管理来自一个或多个外围设备的内存访问请求.每个流总共可 ...