1.什么是分而治之

分而治之就是将一个大任务层层拆分成一个个的小任务,直到不可拆分,拆分依据定义的阈值划分任务规模。

fork/join通过fork将大任务拆分成小任务,在将小任务的结果join汇总

2.fork/join标准范式

先上图

在使用fork/join做任务分配之前,首先得了解其中的几个类:

ForkJoinPool:充当fork/join框架里面的管理者,最原始的任务都要交给它才能处理。它负责控制整个fork/join有多少个workerThread,workerThread的创建,激活都是由它来掌控。它还负责workQueue队列的创建和分配,每当创建一个workerThread,它负责分配相应的workQueue。然后它把接到的活都交给workerThread去处理,它可以说是整个frok/join的容器。

ForkJoinWorkerThread:fork/join里面真正干活的"工人",本质是一个线程。里面有一个ForkJoinPool.WorkQueue的队列存放着它要干的活,接活之前它要向ForkJoinPool注册(registerWorker),拿到相应的workQueue。然后就从workQueue里面拿任务出来处理。它是依附于ForkJoinPool而存活,如果ForkJoinPool的销毁了,它也会跟着结束。

ForkJoinPool.WorkQueue: 双端队列就是它,它负责存储接收的任务。

ForkJoinTask:代表fork/join里面任务类型,我们一般用它的两个子类RecursiveTask、RecursiveAction。这两个区别在于RecursiveTask任务是有返回值,RecursiveAction没有返回值。任务的处理逻辑包括任务的切分都集中在compute()方法里面。

3.废话不多说,代码走起

fork/join在平时的使用过程中,一般分为同步调用和异步调用,下面是两种情况的实例:


/**同步用法*/
1 public class SumArray {
2 private static class SumTask extends RecursiveTask<Integer>{
3
4 private final static int THRESHOLD = MakeArray.ARRAY_LENGTH/10;
5 private int[] src; //表示我们要实际统计的数组
6 private int fromIndex;//开始统计的下标
7 private int toIndex;//统计到哪里结束的下标
8
9 public SumTask(int[] src, int fromIndex, int toIndex) {
10 this.src = src;
11 this.fromIndex = fromIndex;
12 this.toIndex = toIndex;
13 }
14
15 @Override
16 protected Integer compute() {
17 if(toIndex-fromIndex < THRESHOLD) {
18 int count = 0;
19 for(int i=fromIndex;i<=toIndex;i++) {
20 //SleepTools.ms(1);
21 count = count + src[i];
22 }
23 return count;
24 }else {
25 //fromIndex....mid....toIndex
26 //1...................70....100
27 int mid = (fromIndex+toIndex)/2;
//将任务一分为二
28 SumTask left = new SumTask(src,fromIndex,mid);
29 SumTask right = new SumTask(src,mid+1,toIndex);
30 invokeAll(left,right); //提交任务
31 return left.join()+right.join();
32 }
33 }
34 }
35
36 public static void main(String[] args) {
37
38 ForkJoinPool pool = new ForkJoinPool();
39 int[] src = MakeArray.makeArray();
40
41 SumTask innerFind = new SumTask(src,0,src.length-1);
42
43 long start = System.currentTimeMillis();
44
45 int a = pool.invoke(innerFind);//同步调用
46 System.out.println("Task is Running.....");
47 System.out.println("The count is "+innerFind.join()
48 +" spend time:"+(System.currentTimeMillis()-start)+"ms"+a);
49 }
50 }
/**
*异步用法
*
*类说明:遍历指定目录(含子目录)找寻指定类型文件
*/
public class FindDirsFiles extends RecursiveAction{ private File path;//当前任务需要搜寻的目录 public FindDirsFiles(File path) {
this.path = path;
} public static void main(String [] args){
try {
// 用一个 ForkJoinPool 实例调度总任务
ForkJoinPool pool = new ForkJoinPool();
FindDirsFiles task = new FindDirsFiles(new File("F:/")); pool.execute(task);//异步调用 System.out.println("Task is Running......");
Thread.sleep(1);
int otherWork = 0;
for(int i=0;i<100;i++){
otherWork = otherWork+i;
}
System.out.println("Main Thread done sth......,otherWork="+otherWork);
task.join();//阻塞的方法
System.out.println("Task end");
} catch (Exception e) {
e.printStackTrace();
}
} @Override
protected void compute() { List<FindDirsFiles> subTasks = new ArrayList<>(); File[] files = path.listFiles();
if(files!=null) {
for(File file:files) {
if(file.isDirectory()) {
subTasks.add(new FindDirsFiles(file));
}else {
//遇到文件,检查
if(file.getAbsolutePath().endsWith("txt")) {
System.out.println("文件:"+file.getAbsolutePath());
}
}
}
if(!subTasks.isEmpty()) {
for(FindDirsFiles subTask:invokeAll(subTasks)) {
subTask.join();//等待子任务执行完成
}
}
} }
}

这段代码可以直接运行试试,跟上面的标准范式一样,在这里我是实现了RecursiveTask(有兴趣的可以自己改成RecursiveAction玩玩,但是RecursiveAction是没有返回值的,使用的时候需要注意),当调用ForkJoinPool的invoke方法启动任务,会同步调用重写的compute方法,这个方法里面才是你要写的fork/join业务代码。

可以看到,我定义了一个阈值THRESHOLD,当任务小于这个阈值的时候,执行运算,否则继续切分任务,提交任务,循环调用,直到任务不可切分,将所有的运算结果整合。其实我在调用invokeAll方法时,并不会立刻返回结果,里面还是会去重复判断每一个任务是否小于阈值,当所有的任务都满足条件并执行完成,才会返回,其实就是递归调用。

总结:

fork/join的使用其实没什么难度,其基本思想是将大任务分割成小任务,最后将小任务聚合起来得到结果。fork是分解的意思, join是收集的意思. 它非常类似于HADOOP提供的MapReduce框架,只是MapReduce的任务可以针对集群内的所有计算节点,可以充分利用集群的能力完成计算任务。ForkJoin更加类似于单机版的MapReduce。

并发编程之fork/join(分而治之)的更多相关文章

  1. 并发编程之Fork/Join

    并发与并行 并发:多个进程交替执行. 并行:多个进程同时进行,不存在线程的上下文切换. 并发与并行的目的都是使CPU的利用率达到最大.Fork/Join就是为了尽可能提高硬件的使用率而应运而生的. 计 ...

  2. java-并发编程之fork/join框架

    Fork/Join框架是Java 7提供的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架.Fork/Join框架要完成两件事情: 1.任务分 ...

  3. c++并发编程之thread::join()和thread::detach()

    thread::join(): 阻塞当前线程,直至 *this 所标识的线程完成其执行.*this 所标识的线程的完成同步于从 join() 的成功返回. 该方法简单暴力,主线程等待子进程期间什么都不 ...

  4. python并发编程之multiprocessing进程(二)

    python的multiprocessing模块是用来创建多进程的,下面对multiprocessing总结一下使用记录. 系列文章 python并发编程之threading线程(一) python并 ...

  5. python并发编程之Queue线程、进程、协程通信(五)

    单线程.多线程之间.进程之间.协程之间很多时候需要协同完成工作,这个时候它们需要进行通讯.或者说为了解耦,普遍采用Queue,生产消费模式. 系列文章 python并发编程之threading线程(一 ...

  6. python并发编程之gevent协程(四)

    协程的含义就不再提,在py2和py3的早期版本中,python协程的主流实现方法是使用gevent模块.由于协程对于操作系统是无感知的,所以其切换需要程序员自己去完成. 系列文章 python并发编程 ...

  7. python并发编程之asyncio协程(三)

    协程实现了在单线程下的并发,每个协程共享线程的几乎所有的资源,除了协程自己私有的上下文栈:协程的切换属于程序级别的切换,对于操作系统来说是无感知的,因此切换速度更快.开销更小.效率更高,在有多IO操作 ...

  8. python并发编程之threading线程(一)

    进程是系统进行资源分配最小单元,线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.进程在执行过程中拥有独立的内存单元,而多个线程共享内存等资源. 系列文章 py ...

  9. 并发编程之:JMM

    并发编程之:JMM 大家好,我是小黑,一个在互联网苟且偷生的农民工. 上一期给大家分享了关于Java中线程相关的一些基础知识.在关于线程终止的例子中,第一个方法讲到要想终止一个线程,可以使用标志位的方 ...

随机推荐

  1. Asp.Net Core仓储模式+工作单元

    仓储模式+工作单元 仓储模式 仓储(Repository)模式自2004年首次作为领域驱动模型DDD设计的一部分引入,仓储本质上是提供提供数据的抽象,以便应用程序可以使用具有接口的相似的简单抽象集合. ...

  2. git初尝

    跨考进科软,要做一个真正的码农了! 怎么能不会用git呢? 感谢孟宁老师带领我们入门.这是梦宁老师的文章:https://mp.weixin.qq.com/s/Km5KuXPETvG0wCGHrvj9 ...

  3. 构建Zookeeper集群(zkcluster) ~一篇文章玩转zk集群^.^

    概念 Zookeeper集群是由一个leader(负责人)主机和多个follower(追随者)或observer(观察者)主机组成. 构建一个Zookeeper集群需要有一个leader和一个foll ...

  4. 有两张表;使用SQL查询,查询所有的客户订单日期最新的前五条订单记录。

    客户信息表(c CUSTOM)有以下字段:id.name.mobile 客户订单表(C_ORDER)有以下字段:id.custom_id.commodity.count.order _date Sel ...

  5. Mapreduce实例--求平均值

    求平均数是MapReduce比较常见的算法,求平均数的算法也比较简单,一种思路是Map端读取数据,在数据输入到Reduce之前先经过shuffle,将map函数输出的key值相同的所有的value值形 ...

  6. mysql explain参数详解

    主要对几个参数做一些记录 type:显示的是访问类型 从最好到最差的连接类型为:const.eq_reg.ref.range.index和ALL 至少要达到range,基本是ref  最好是const ...

  7. FAAS -- Serverless

    FAAS概念,无服务器运算,功能即服务,function-as-a-service 初创企业-大型企业.民间组织-政府机构 ===>>>> 上云 云计算第三代技术 -- Ser ...

  8. Solon rpc 之 SocketD 协议 - 单链接双向RPC模式

    Solon rpc 之 SocketD 协议系列 Solon rpc 之 SocketD 协议 - 概述 Solon rpc 之 SocketD 协议 - 消息上报模式 Solon rpc 之 Soc ...

  9. 用js实现打印九九乘法表

    用js在打印九九乘法表 思考 在学习了流程控制和条件判断后,我们可以利用js打印各式各样的九九乘法表 不管是打印什么样三角形九九乘法表,我们都应该找到有规律的地方,比如第一列的数字是什么规律,第一行的 ...

  10. python_元组(tuple)

    #tuple(),元组不可以修改,不能对其进行增加或删除操作,元组是有序的 #1.定义 tu_1 = () #定义一个空元组 tu_2 = (1,2,'alex',[3,4],(5,6,7),True ...