ForkJoinPool 是jdk1.7 由Doug Lea 写的实现   递归调用任务拆分,合并,的线程池。

代码示例:

  1. package www.itbac.com;
  2.  
  3. import com.alibaba.fastjson.JSONObject;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.stereotype.Service;
  6. import org.springframework.web.client.RestTemplate;
  7.  
  8. import java.util.ArrayList;
  9. import java.util.concurrent.*;
  10.  
  11. /**
  12. * 并行调用http接口
  13. */
  14. @Service
  15. public class UserServiceForkJoin {
  16. // 本质是一个线程池,默认的线程数量:CPU的核数
  17. ForkJoinPool forkJoinPool = new ForkJoinPool(10, ForkJoinPool.defaultForkJoinWorkerThreadFactory,
  18. null, true);
  19. @Autowired
  20. private RestTemplate restTemplate;
  21.  
  22. /**
  23. * 查询多个系统的数据,合并返回
  24. */
  25. public Object getUserInfo(String userId) throws ExecutionException, InterruptedException {
  26. // 其他例子, 查数据库的多个表数据,分多次查询
  27. // fork/join
  28. // forkJoinPool.submit()
  29. ArrayList<String> urls = new ArrayList<>();
  30. urls.add("http://www.itbac.com/userinfo-api/get?userId=" + userId);
  31. urls.add("http://www.itbac.com/integral-api/get?userId=" + userId);
  32.  
  33. HttpJsonRequest httpJsonRequest = new HttpJsonRequest(restTemplate, urls, 0, urls.size() - 1);
  34. ForkJoinTask<JSONObject> forkJoinTask = forkJoinPool.submit(httpJsonRequest);
  35.  
  36. JSONObject result = forkJoinTask.get();
  37. return result;
  38. }
  39. }
  40.  
  41. // 自定义任务类, 继承递归任务。
  42. class HttpJsonRequest extends RecursiveTask<JSONObject> {
  43.  
  44. RestTemplate restTemplate;
  45. ArrayList<String> urls;
  46. int start;
  47. int end;
  48.  
  49. HttpJsonRequest(RestTemplate restTemplate, ArrayList<String> urls, int start, int end) {
  50. this.restTemplate = restTemplate;
  51. this.urls = urls;
  52. this.start = start;
  53. this.end = end;
  54. }
  55.  
  56. // 就是实际去执行的一个方法入口(任务拆分)
  57. @Override
  58. protected JSONObject compute() {
  59. int count = end - start; // 代表当前这个task需要处理多少数据
  60. // 自行根据业务场景去判断是否是大任务,是否需要拆分
  61. if (count == 0) {
  62. String url = urls.get(start);
  63. // TODO 如果只有一个接口调用,立刻调用
  64. long userinfoTime = System.currentTimeMillis();
  65. String response = restTemplate.getForObject(url, String.class);
  66. JSONObject value = JSONObject.parseObject(response);
  67. System.out.println(Thread.currentThread() + " 接口调用完毕" + (System.currentTimeMillis() - userinfoTime) + " #" + url);
  68. return value;
  69. } else { // 如果是多个接口调用,拆分成子任务 7,8, 9,10
  70. System.out.println(Thread.currentThread() + "任务拆分一次");
  71. //求中间值。
  72. int x = (start + end) / 2;
  73. //任务从开始,到中间值。
  74. HttpJsonRequest httpJsonRequest = new HttpJsonRequest(restTemplate, urls, start, x);// 负责处理哪一部分?
  75. //fork拆分任务。
  76. httpJsonRequest.fork();
  77. //任务从中间值+1 ,到结束。
  78. HttpJsonRequest httpJsonRequest1 = new HttpJsonRequest(restTemplate, urls, x + 1, end);// 负责处理哪一部分?
  79. httpJsonRequest1.fork();
  80.  
  81. // join获取处理结果
  82. JSONObject result = new JSONObject();
  83.  
  84. //join合并结果。
  85. result.putAll(httpJsonRequest.join());
  86. result.putAll(httpJsonRequest1.join());
  87.  
  88. return result;
  89. }
  90. }
  91. }

就是把任务拆分,交给线程池执行,再合并。与Future的获取返回值有点相似。只是对任务拆分做了抽象封装。

特点:

线程池 ThreadPoolExecutor 中只维护了一个队列。多线程去队列中争抢任务来执行。

而ForkJoinPool 是每一个大任务是维护一个队列,fork拆分出的小任务也是在自己队列中。一个线程去处理自己队列中的任务,此时,没有线程争抢,效率比线程池要高。

该线程把当前自己的队列处理完了,就去和其他线程争抢其他队列的任务来处理,这个术语叫工作窃取work-stealing .

ForkJoinPool 维护了多个队列,ThreadPoolExecutor只维护了一个队列,通过多个队列来减少线程争抢,从而提高了效率。

但是:

每个worker线程都维护一个任务队列,ForkJoinWorkerThread中的任务队列。当这个worker线程处理完自己队列的任务,会随机从其他的worker的队列中拿走一个任务执行(工作窃取:work-stealing )。

如果所有worker线程都很忙,大家都没有工作窃取,那就是单线程处理完整个任务队列。对于请求方而言,本次任务拆分,并没有提高响应的效率?

而且,如果任务拆分太细,递归调用太深,这个拆分,合并,的过程,也是消耗性能的。

结语:

ForkJoinPool的工作窃取带来的性能提升偏理论,API的源码复杂度较高,实际研发中可控性来说不如其他API ,谨慎使用。

使用ForkJoinPool来多线程的拆分任务,执行任务,合并结果。的更多相关文章

  1. java一个大接口拆用多线程方式拆分成多个小接口

    问题引入 目的:我们的接口A  分别调用了a1 a2 a3 三个接口,最终返回值是 a1的返回值+a2的返回值+a3的返回值 如果同步执行 a1 a2 a3 然后结果相加 很慢 . 如果异步执行 无法 ...

  2. java 多线程Callable和Runable执行顺序问题详解

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt125 毫无疑问 Runnable会进行异步执行,此处不多说,主要说明Call ...

  3. [JAVA]多线程下如何确定执行顺序性

    最近在讨论一个下载任务:要求文件下载后进行打包,再提供给用户下载: 如何确保打包的线程在所有下载文件的线程执行完成后进行呢? 看看下面三个兄弟的本事: CountDownLatch.CyclicBar ...

  4. [Python 多线程] Timer定时器/延迟执行、Event事件 (七)

    Timer继承子Thread类,是Thread的子类,也是线程类,具有线程的能力和特征.这个类用来定义多久执行一个函数. 它的实例是能够延迟执行目标函数的线程,在真正执行目标函数之前,都可以cance ...

  5. 【多线程】线程强制执行 join()

    线程强制执行 join() Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞 : 可以想象成插队. 代码示例: /** * @Description 测试join方法 * @Auth ...

  6. Java多线程,线程交替执行

    两个线程,一个打印1-100的奇数,一个打印1-100的偶数:要求:线程1打印5个之后,线程2开始打印,线程2打印5个之后,线程1再开始打印,以此循环. Code: package com.qhong ...

  7. 多线程时,请求执行不是按顺序的,可添加Critical Section Controller(临界部分控制器),执行顺序是固定的,但执行一段时间后,该逻辑器下的请求不再循环,无解ing

  8. oracle 字符串 正则表达式 拆分,排序,合并

    需求,表数据如:要求圈中的数据,必须根据线芯有序排列. 思路: 1.首先根据分号分隔元素.oracle 很蛋疼,没有提供字符串分隔函数,网上倒是多觉得有点麻烦,耐着性子继续网上找了下,还真让我找到一篇 ...

  9. Flask整合WebLoader 用于大附件拆分上传再合并

    博客:https://blog.csdn.net/jinixin/article/details/77545140 github:https://github.com/jinixin/upload-d ...

随机推荐

  1. Mysql事务隔离级别和锁机制

    一.Spring支持四种事务隔离级别: 1.ISOLATION_READ_UNCOMMITTED(读未提交):这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据. 2.ISOLAT ...

  2. Android中控件属性详细总结(转载)

    转载地址:https://www.cnblogs.com/nanguojs/p/5950510.html 1.LinearLayout(线性布局): 可以分为水平线性:android:orientat ...

  3. 统一资源定位符URL

    Uniform Resource Locate--URL 用途:通过URL访问web网页:通过URL使用其它的Internet应用程序,例如FTP,Telnet(远程登录服务):对应IE浏览器窗口中的 ...

  4. Python笔记【5】_字符串&列表&元组&字典之间转换学习

    #!/usr/bin/env/python #-*-coding:utf-8-*- #Author:LingChongShi #查看源码Ctrl+左键 #数据类型之间的转换 Str='www.baid ...

  5. 前端摸爬滚打之路(一)之 JavaScript 基础

    前言:这是我第一次在博客上记录自己的前端学习过程,以往都是在桌面右侧开个 onenote 小窗,记录自己在学习过程中获得的知识.通常都是记录的满满当当,然后心满意足的关闭窗口,但是记录不代表学会.这些 ...

  6. Codeforces Gym101201B:Buggy Robot(BFS + DP)

    题目链接 题意 给出一个n*m的地图,还有一个操作序列,你原本是要按照序列执行操作的,但是你可以修改操作:删除某些操作或者增加某些操作,问从'R'到'E'最少需要多少次修改操作. 思路 和上次比赛做的 ...

  7. vue中局部组件的使用

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. 浅谈SpringBoot

    关于SpringBoot有哪些特性,SpringBoot官网是这么描述的: Features Create stand-alone Spring applications Embed Tomcat, ...

  9. Linux部署项目常用命令

    前言:一般项目都会使用阿里云等服务器作为云服务器.此时必不可免会使用到一系列常用的命令.这里我整合一下常用的命令 1.一般链接阿里云服务器常用的的是xshell跟xftp. 下载路径:https:// ...

  10. 不要再问我Java程序是怎么执行的了!

    什么是Java虚拟机? 要弄明白Java程序的执行过程首先要了解一下Java虚拟机 虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的.Java虚拟机有自己完善的硬体架构, ...