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. 从零开始搭建系统2.8——HDFS安装及配置

    从零开始搭建系统2.8——HDFS安装及配置

  2. web.xml中配置——加载spring容器

    <context-param> <param-name>contextConfigLocation</param-name> <param-value> ...

  3. ApiCloud如何一键真机测试

    首先假设你已经有了APICloud账号,并创建了App项目. 需要注意的是,手机和电脑需要连接在同一wifi环境下. 第一步 下载自定义Loader 进入“开发控制台” 点击我们的项目 选择模块,再选 ...

  4. SQL 更新

    SQL UPDATE 语句(更新表中的记录) UPDATE 语句用于更新表中的现有记录. SQL UPDATE 语句 UPDATE 语句用于更新表中已存在的记录. SQL UPDATE 语法 UPDA ...

  5. 帝国CMS数据库数据表详细说明

    表名                                             解释 phome_ecms_infoclass_news 新闻采集规则记录表 phome_ecms_inf ...

  6. 构造——cf1214E

    首先定义三元组(2*i-1,2*i,d),将n个三元组按d降序排列,然后取每个三元组的2*i-1连在一起,构成一条长为n-1的链 然后降序枚举每个三元组,把对应的2*i在链上合法的位置接上, 能保证一 ...

  7. java EE学习流程(第二版更新)

    这周有点堕落了,这两天啥都没写,就顾上刷<庆余年>了

  8. delphi与javascript互通

    unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms ...

  9. 使用Objective-C的+(void)initialize初始化static变量

    在<Objective C类方法load和initialize的区别>一文中,我介绍了Objective-C对待+(void)initialize和+(void)load两个方法在编译和执 ...

  10. 数据可视化(Echart) :柱状图、折线图、饼图等六种基本图表的特点及适用场合

    数据可视化(Echart) 柱状图.折线图.饼图等六种基本图表的特点及适用场合 参考网址 效果图 源码 <!DOCTYPE html> <html> <head> ...