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. JMeter 并发压力测试

    一,下载JMeter http://jmeter.apache.org/download_jmeter.cgi 二,创建默认配置 可以不用配置相同参数. 测试计划:右键添加线程组 线程组:右键添加  ...

  2. C—变量

    C—变量 在C语言中,变量要先定义后使用. 使用时,必须说明变量的存储类型与数据类型. 变量说明的一般形式: <存储类型>  <数据类型>  <变量名> 存储类型的 ...

  3. mac 命令行

    本文为使用到过的命令行,仅方便自己查阅 1.进入目录文件 cd name name为文件名 2.返回上一级目录 cd ../ 3.创建文件夹 mkdir name 4.删除文件夹(文件夹下不能包含文件 ...

  4. Vue学习笔记【24】——Vue组件(组件切换)

    使用flag标识符结合v-if和v-else切换组件 页面结构:(缺点:只适用于两个组件间切换,不适合多个)  <div id="app">    <input ...

  5. Python100天打卡

    基于tkinter模块的GUIPython默认的GUI开发模块是tkinter(在Python 3以前的版本中名为Tkinter)使用tkinter来开发GUI应用需要以下5个步骤: 导入tkinte ...

  6. yield列表反转 islice切片(2.6)

    yield列表反转 islice切片 列表反转 l1 = [i for i in range(10)] print(l1) print(l1[::2]) l1.reverse() # 注: pytho ...

  7. qemu的动态翻译机制

    qemu的作者在QEMU, a Fast and Portable Dynamic Translator一文提到了qemu的动态翻译机制, 大致可以总结为如下过程: 目标代码中的一条指令 | |--( ...

  8. CPUID读取有关Cache的信息

    1: void cpuidTest() 2: { 3: u32 val_eax, val_ebx, val_ecx, val_edx; 4: asm("cpuid" 5: : &q ...

  9. UVA 10522 Height to Area(知三角形三高求面积)

    思路:海伦公式, AC代码: #include<bits/stdc++.h> using namespace std; int main() { int n; scanf("%d ...

  10. mysql中explain详解

    explain语法 有两种用法: 1.EXPLAIN tbl_name    2.EXPLAIN [EXTENDED] SELECT select_options 为了更好的说明它,我们需要建两张表, ...