1. Fork/Join 的概念

  Fork指的是将系统进程分成多个执行分支(线程),Join即是等待,当fork()方法创建了多个线程之后,需要等待这些分支执行完毕之后,才能得到最终的结果,因此join就表示等待。在实际的使用中,如果毫无顾忌的使用fork()来开启线程,可能会由于线程过多而影响系统性能,因此在jdk中提供了ForkJoinPool线程池来对fork进行处理,以节省资源。

  对于线程池来说,提交的任务数量并不总是与线程数相等的,大多数情况下一个物理线程可能需要执行多个逻辑任务。所以每个线程必然会有一个任务队列。在实际的执行过程中,可能会出现A线程已经执行完成队列中的所有任务了,但是B线程中还有很多任务等着执行,此时A线程就会从B线程中拿到任务过来处理,尽可能的达到平衡。需要注意的是,当线程开始帮助别的线程执行任务时,总会从其他的线程任务队列的底部开始拿,而线程执行自己任务的时候,总会从队列的顶部开始拿,这样就你能有效的避免了线程之间数据的竞争。

2. 使用方式

  在看使用方式之前,先来看ForkJoinPool的一个重要的接口:

  

 public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task)

  它接收一个ForkJoinTask作为任务,ForkJoinTask就是持有fork()分解和join()等待的任务。它有两个重要的子类,即RecursiveTask有返回值任务 和RecursiveAction无返回值任务,在使用的时候只需要根据场景继承它的的两个子类之一即可。示例代码为带返回值的任务的使用方式:

package com.wangx.thread.t7;

import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask; /**
* 求和
*/
public class CountTask extends RecursiveTask<Long> { //任务分配标准
private static final int THRESHOLD = 1000;
private long start;
private long end; public CountTask(long start, long end) {
this.start = start;
this.end = end;
} /**
* 分而治之,分成多个小任务执行,最后再执行汇总
* @return
*/
@Override
public Long compute() {
long sum = 0;
boolean canCompute = (end - start) < THRESHOLD;
//开始和结束之间间隔小于1000
if (canCompute) {
//执行数据求和
for (long i = start; i <= end; i++) {
sum += i;
}
} else {
//分成100个小任务
long step = (start + end) / 100;
ArrayList<CountTask> countTaskList = new ArrayList<>(); long pos = start; for (int i = 0; i < 100; i++) {
long lastOne = pos + step;
CountTask task = new CountTask(pos, lastOne);
pos += step+1;
countTaskList.add(task);
//开启子任务
task.fork();
}
//等待所有子任务都执行完毕后再对子任务进行求和
for (CountTask countTask : countTaskList) {
sum += countTask.join();
}
}
return sum;
} public static void main(String[] args) {
ForkJoinPool forkJoinPool = new ForkJoinPool(); CountTask countTask = new CountTask(0, 20000L); ForkJoinTask<Long> forkJoinTask = forkJoinPool.submit(countTask); try {
long res = forkJoinTask.get();
System.out.println("sum=" + res );
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}

  该示例是用于计算数列和的,必然是需要返回值的,正好可以继承RecursiveTask的方式来使用,使用forkJoinPool.submit()将任务交给线程池,线程池返回一个ForkJoinTask对象,调用该对象的get()方法获取结果,当在执行get()方法时任务没有执行完成,则主线程会一直等待所有线程执行完成。

  compute()方法主要对任务进行分解,当求和数量大于THRESHOLD个时,就需要再次分解任务,否则直接求和,分解任务时,简单的将原有的任务分成100个小任务,并使用fork()方法提交,再然后调用join()方法等待所有任务执行完成后,最后对每个子任务的结果再次进行求和,得到最终结果。

原文 并发编程学习笔记(12)----Fork/Join框架

并发编程学习笔记(12)----Fork/Join框架的更多相关文章

  1. 并发编程学习笔记(15)----Executor框架的使用

    Executor执行已提交的 Runnable 任务的对象.此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节.调度等)分离开来的方法.通常使用 Executor 而不是显式地创建 ...

  2. JUC并发编程学习笔记

    JUC并发编程学习笔记 狂神JUC并发编程 总的来说还可以,学到一些新知识,但很多是学过的了,深入的部分不多. 线程与进程 进程:一个程序,程序的集合,比如一个音乐播发器,QQ程序等.一个进程往往包含 ...

  3. Java并发编程学习笔记

    Java编程思想,并发编程学习笔记. 一.基本的线程机制 1.定义任务:Runnable接口 线程可以驱动任务,因此需要一种描述任务的方式,这可以由Runnable接口来提供.要想定义任务,只需实现R ...

  4. 并发编程学习笔记(9)----AQS的共享模式源码分析及CountDownLatch使用及原理

    1. AQS共享模式 前面已经说过了AQS的原理及独享模式的源码分析,今天就来学习共享模式下的AQS的几个接口的源码. 首先还是从顶级接口acquireShared()方法入手: public fin ...

  5. 并发编程学习笔记(6)----公平锁和ReentrantReadWriteLock使用及原理

    (一)公平锁 1.什么是公平锁? 公平锁指的是在某个线程释放锁之后,等待的线程获取锁的策略是以请求获取锁的时间为标准的,即使先请求获取锁的线程先拿到锁. 2.在java中的实现? 在java的并发包中 ...

  6. 并发编程学习笔记(5)----AbstractQueuedSynchronizer(AQS)原理及使用

    (一)什么是AQS? 阅读java文档可以知道,AbstractQueuedSynchronizer是实现依赖于先进先出 (FIFO) 等待队列的阻塞锁和相关同步器(信号量.事件,等等)提供一个框架, ...

  7. 并发编程学习笔记(14)----ThreadPoolExecutor(线程池)的使用及原理

    1. 概述 1.1 什么是线程池 与jdbc连接池类似,在创建线程池或销毁线程时,会消耗大量的系统资源,因此在java中提出了线程池的概念,预先创建好固定数量的线程,当有任务需要线程去执行时,不用再去 ...

  8. 并发编程学习笔记(13)----ConcurrentLinkedQueue(非阻塞队列)和BlockingQueue(阻塞队列)原理

    · 在并发编程中,我们有时候会需要使用到线程安全的队列,而在Java中如果我们需要实现队列可以有两种方式,一种是阻塞式队列.另一种是非阻塞式的队列,阻塞式队列采用锁来实现,而非阻塞式队列则是采用cas ...

  9. 并发编程学习笔记(11)----FutureTask的使用及实现

    1. Future的使用 Future模式解决的问题是.在实际的运用场景中,可能某一个任务执行起来非常耗时,如果我们线程一直等着该任务执行完成再去执行其他的代码,就会损耗很大的性能,而Future接口 ...

随机推荐

  1. 使用Docker部署Gitlab

    由于公司的代码server已使用Gitosis搭建,但由于用户和权限管理太麻烦. 如今想在原有server上再搭建Gitlab,使用Gitlab官方方法直接安装. 会导致与Gitosis冲突,使得Gi ...

  2. js的调用函数前先执行某语句问题

    js的调用函数前先执行某语句问题 标签: web前端面试 2015-09-29 17:48 1455人阅读 评论(0) 收藏 举报  分类: js(5)  版权声明:本文为博主原创文章,未经博主允许不 ...

  3. linux 线程同步(二)

    信号量 信号量是相互排斥锁的升级版把相互排斥锁中1变成了n.举个简单的样例:如果如今有10个人,有一部手机.这10个人都竞争来使用手机打电话这就是相互排斥锁.对于信号量,如今可能是有4部手机,这10个 ...

  4. 动态生成页面(一)——ASP.NET中Literal使用

    在页面中加入内容时,假设是静态内容.无需使用容器,能够直接将标记作为HTML直接加入到页面中:可是,假设是动态内容,则必须借助容器将内容加入到页面中.典型的容器有:Label控件.Literal控件. ...

  5. oracle type类型

    转载 http://blog.sina.com.cn/s/blog_6cfb6b090100ve92.html 转自网络,具体用法我会再细化 1.概念    方法:是在对象类型说明中用关键字  MEM ...

  6. mac ox终端显示 bogon的问题

    mac终端显示如果正常应该是电脑名称(偏好设置-共享)的,但是细心的我们会发现,当电脑换了网络环境之后,可能就会出现终端上显示为主机名为:bogon,一查bogon这个单词是虚拟.虚伪的意思.     ...

  7. 【POJ 1679】 The Unique MST

    [题目链接] 点击打开链接 [算法] 先求出图的最小生成树 枚举不在最小生成树上的边,若加入这条边,则形成了一个环,如果在环上且在最小生成树上的权值最大的边等于 这条边的权值,那么,显然最小生成树不唯 ...

  8. Flask的配置文件 与 session

    配置文件 flask中的配置文件是一个flask.config.Config对象(继承字典) 默认配置为: { 'DEBUG': get_debug_flag(default=False), 是否开启 ...

  9. SQLALchemy之创建表,删除表

    1.创建引擎 "数据库+第三方模块://用户名:密码@数据库服务端IP:端口号/数据库名?编码" engine = create_engine( "mysql+pymys ...

  10. RDA DEBUG

    DEBUG寄存器:word 0xa0000010 word 0xa0000010 1 //debug开 word 0xa0000010 0 //debug关 当然也可以按模块打开/关闭debug信息, ...