线程池 一 ForkJoinPool
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中的任务分为两种:
- 一种是本地提交的任务(Submission task,如 execute、submit 提交的任务)
- 另外一种是 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的更多相关文章
- 0041 Java学习笔记-多线程-线程池、ForkJoinPool、ThreadLocal
什么是线程池 创建线程,因为涉及到跟操作系统交互,比较耗费资源.如果要创建大量的线程,而每个线程的生存期又很短,这时候就应该使用线程池了,就像数据库的连接池一样,预先开启一定数量的线程,有任务了就将任 ...
- 【转】线程及同步的性能 - 线程池 / ThreadPoolExecutors / ForkJoinPool
线程池和ThreadPoolExecutors 虽然在程序中可以直接使用Thread类型来进行线程操作,但是更多的情况是使用线程池,尤其是在Java EE应用服务器中,一般会使用若干个线程池来处理来自 ...
- 线程及同步的性能 – 线程池/ ThreadPoolExecutors/ ForkJoinPool
线程池和ThreadPoolExecutors 虽然在程序中可以直接使用Thread类型来进行线程操作,但是更多的情况是使用线程池,尤其是在Java EE应用服务器中,一般会使用若干个线程池来处理来自 ...
- [Java Performance] 线程及同步的性能之线程池/ThreadPoolExecutors/ForkJoinPool
线程池和ThreadPoolExecutors 虽然在程序中可以直接使用Thread类型来进行线程操作,但是更多的情况是使用线程池,尤其是在Java EE应用服务器中,一般会使用若干个线程池来处理 ...
- (四)juc线程高级特性——线程池 / 线程调度 / ForkJoinPool
13. 线程池 第四种获取线程的方法:线程池,一个 ExecutorService,它使用可能的几个池线程之一执行每个提交的任务,通常使用 Executors 工厂方法配置. 线程池可以解决两个不同问 ...
- Java并发包线程池之ForkJoinPool即ForkJoin框架(二)
前言 前面介绍了ForkJoinPool相关的两个类ForkJoinTask.ForkJoinWorkerThread,现在开始了解ForkJoinPool.ForkJoinPool也是实现了Exec ...
- Java并发包线程池之ForkJoinPool即ForkJoin框架(一)
前言 这是Java并发包提供的最后一个线程池实现,也是最复杂的一个线程池.针对这一部分的代码太复杂,由于目前理解有限,只做简单介绍.通常大家说的Fork/Join框架其实就是指由ForkJoinPoo ...
- [原创] JAVA 递归线程池测试 ExecutorService / ForkJoinPool
测试工具使用递归的方式获取子进程的Msg消息,目前有2种常用的ExecutorService / ForkJoinPool 为了测试哪种效果较好,我们来写个测试Demo,循环5555555次+1(加锁 ...
- JAVA 递归线程池测试 ExecutorService / ForkJoinPool
测试工具使用递归的方式获取子进程的Msg消息,目前有2种常用的ExecutorService / ForkJoinPool 为了测试哪种效果较好,我们来写个测试Demo,循环5555555次+1(加锁 ...
随机推荐
- JMeter 并发压力测试
一,下载JMeter http://jmeter.apache.org/download_jmeter.cgi 二,创建默认配置 可以不用配置相同参数. 测试计划:右键添加线程组 线程组:右键添加 ...
- C—变量
C—变量 在C语言中,变量要先定义后使用. 使用时,必须说明变量的存储类型与数据类型. 变量说明的一般形式: <存储类型> <数据类型> <变量名> 存储类型的 ...
- mac 命令行
本文为使用到过的命令行,仅方便自己查阅 1.进入目录文件 cd name name为文件名 2.返回上一级目录 cd ../ 3.创建文件夹 mkdir name 4.删除文件夹(文件夹下不能包含文件 ...
- Vue学习笔记【24】——Vue组件(组件切换)
使用flag标识符结合v-if和v-else切换组件 页面结构:(缺点:只适用于两个组件间切换,不适合多个) <div id="app"> <input ...
- Python100天打卡
基于tkinter模块的GUIPython默认的GUI开发模块是tkinter(在Python 3以前的版本中名为Tkinter)使用tkinter来开发GUI应用需要以下5个步骤: 导入tkinte ...
- yield列表反转 islice切片(2.6)
yield列表反转 islice切片 列表反转 l1 = [i for i in range(10)] print(l1) print(l1[::2]) l1.reverse() # 注: pytho ...
- qemu的动态翻译机制
qemu的作者在QEMU, a Fast and Portable Dynamic Translator一文提到了qemu的动态翻译机制, 大致可以总结为如下过程: 目标代码中的一条指令 | |--( ...
- CPUID读取有关Cache的信息
1: void cpuidTest() 2: { 3: u32 val_eax, val_ebx, val_ecx, val_edx; 4: asm("cpuid" 5: : &q ...
- UVA 10522 Height to Area(知三角形三高求面积)
思路:海伦公式, AC代码: #include<bits/stdc++.h> using namespace std; int main() { int n; scanf("%d ...
- mysql中explain详解
explain语法 有两种用法: 1.EXPLAIN tbl_name 2.EXPLAIN [EXTENDED] SELECT select_options 为了更好的说明它,我们需要建两张表, ...