使用ForkJoinPool来多线程的拆分任务,执行任务,合并结果。
ForkJoinPool 是jdk1.7 由Doug Lea 写的实现 递归调用任务拆分,合并,的线程池。
代码示例:
package www.itbac.com; import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate; import java.util.ArrayList;
import java.util.concurrent.*; /**
* 并行调用http接口
*/
@Service
public class UserServiceForkJoin {
// 本质是一个线程池,默认的线程数量:CPU的核数
ForkJoinPool forkJoinPool = new ForkJoinPool(10, ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
@Autowired
private RestTemplate restTemplate; /**
* 查询多个系统的数据,合并返回
*/
public Object getUserInfo(String userId) throws ExecutionException, InterruptedException {
// 其他例子, 查数据库的多个表数据,分多次查询
// fork/join
// forkJoinPool.submit()
ArrayList<String> urls = new ArrayList<>();
urls.add("http://www.itbac.com/userinfo-api/get?userId=" + userId);
urls.add("http://www.itbac.com/integral-api/get?userId=" + userId); HttpJsonRequest httpJsonRequest = new HttpJsonRequest(restTemplate, urls, 0, urls.size() - 1);
ForkJoinTask<JSONObject> forkJoinTask = forkJoinPool.submit(httpJsonRequest); JSONObject result = forkJoinTask.get();
return result;
}
} // 自定义任务类, 继承递归任务。
class HttpJsonRequest extends RecursiveTask<JSONObject> { RestTemplate restTemplate;
ArrayList<String> urls;
int start;
int end; HttpJsonRequest(RestTemplate restTemplate, ArrayList<String> urls, int start, int end) {
this.restTemplate = restTemplate;
this.urls = urls;
this.start = start;
this.end = end;
} // 就是实际去执行的一个方法入口(任务拆分)
@Override
protected JSONObject compute() {
int count = end - start; // 代表当前这个task需要处理多少数据
// 自行根据业务场景去判断是否是大任务,是否需要拆分
if (count == 0) {
String url = urls.get(start);
// TODO 如果只有一个接口调用,立刻调用
long userinfoTime = System.currentTimeMillis();
String response = restTemplate.getForObject(url, String.class);
JSONObject value = JSONObject.parseObject(response);
System.out.println(Thread.currentThread() + " 接口调用完毕" + (System.currentTimeMillis() - userinfoTime) + " #" + url);
return value;
} else { // 如果是多个接口调用,拆分成子任务 7,8, 9,10
System.out.println(Thread.currentThread() + "任务拆分一次");
//求中间值。
int x = (start + end) / 2;
//任务从开始,到中间值。
HttpJsonRequest httpJsonRequest = new HttpJsonRequest(restTemplate, urls, start, x);// 负责处理哪一部分?
//fork拆分任务。
httpJsonRequest.fork();
//任务从中间值+1 ,到结束。
HttpJsonRequest httpJsonRequest1 = new HttpJsonRequest(restTemplate, urls, x + 1, end);// 负责处理哪一部分?
httpJsonRequest1.fork(); // join获取处理结果
JSONObject result = new JSONObject(); //join合并结果。
result.putAll(httpJsonRequest.join());
result.putAll(httpJsonRequest1.join()); return result;
}
}
}
就是把任务拆分,交给线程池执行,再合并。与Future的获取返回值有点相似。只是对任务拆分做了抽象封装。
特点:
线程池 ThreadPoolExecutor 中只维护了一个队列。多线程去队列中争抢任务来执行。
而ForkJoinPool 是每一个大任务是维护一个队列,fork拆分出的小任务也是在自己队列中。一个线程去处理自己队列中的任务,此时,没有线程争抢,效率比线程池要高。
该线程把当前自己的队列处理完了,就去和其他线程争抢其他队列的任务来处理,这个术语叫工作窃取work-stealing .
ForkJoinPool 维护了多个队列,ThreadPoolExecutor只维护了一个队列,通过多个队列来减少线程争抢,从而提高了效率。
但是:
每个worker线程都维护一个任务队列,ForkJoinWorkerThread中的任务队列。当这个worker线程处理完自己队列的任务,会随机从其他的worker的队列中拿走一个任务执行(工作窃取:work-stealing )。
如果所有worker线程都很忙,大家都没有工作窃取,那就是单线程处理完整个任务队列。对于请求方而言,本次任务拆分,并没有提高响应的效率?
而且,如果任务拆分太细,递归调用太深,这个拆分,合并,的过程,也是消耗性能的。
结语:
ForkJoinPool的工作窃取带来的性能提升偏理论,API的源码复杂度较高,实际研发中可控性来说不如其他API ,谨慎使用。
使用ForkJoinPool来多线程的拆分任务,执行任务,合并结果。的更多相关文章
- java一个大接口拆用多线程方式拆分成多个小接口
问题引入 目的:我们的接口A 分别调用了a1 a2 a3 三个接口,最终返回值是 a1的返回值+a2的返回值+a3的返回值 如果同步执行 a1 a2 a3 然后结果相加 很慢 . 如果异步执行 无法 ...
- java 多线程Callable和Runable执行顺序问题详解
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt125 毫无疑问 Runnable会进行异步执行,此处不多说,主要说明Call ...
- [JAVA]多线程下如何确定执行顺序性
最近在讨论一个下载任务:要求文件下载后进行打包,再提供给用户下载: 如何确保打包的线程在所有下载文件的线程执行完成后进行呢? 看看下面三个兄弟的本事: CountDownLatch.CyclicBar ...
- [Python 多线程] Timer定时器/延迟执行、Event事件 (七)
Timer继承子Thread类,是Thread的子类,也是线程类,具有线程的能力和特征.这个类用来定义多久执行一个函数. 它的实例是能够延迟执行目标函数的线程,在真正执行目标函数之前,都可以cance ...
- 【多线程】线程强制执行 join()
线程强制执行 join() Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞 : 可以想象成插队. 代码示例: /** * @Description 测试join方法 * @Auth ...
- Java多线程,线程交替执行
两个线程,一个打印1-100的奇数,一个打印1-100的偶数:要求:线程1打印5个之后,线程2开始打印,线程2打印5个之后,线程1再开始打印,以此循环. Code: package com.qhong ...
- 多线程时,请求执行不是按顺序的,可添加Critical Section Controller(临界部分控制器),执行顺序是固定的,但执行一段时间后,该逻辑器下的请求不再循环,无解ing
- oracle 字符串 正则表达式 拆分,排序,合并
需求,表数据如:要求圈中的数据,必须根据线芯有序排列. 思路: 1.首先根据分号分隔元素.oracle 很蛋疼,没有提供字符串分隔函数,网上倒是多觉得有点麻烦,耐着性子继续网上找了下,还真让我找到一篇 ...
- Flask整合WebLoader 用于大附件拆分上传再合并
博客:https://blog.csdn.net/jinixin/article/details/77545140 github:https://github.com/jinixin/upload-d ...
随机推荐
- Centos7.6 双网卡,修改默认路由
centos修改默认路由 需求:服务器有两个网卡,一个是内网网卡,一个是外网网卡,但是默认的路由是内网: route -n 列出所有路由: 删除默认的路由: route del default gw ...
- 机器学习经典算法之Apriori
一. 搞懂关联规则中的几个概念 关联规则这个概念,最早是由 Agrawal 等人在 1993 年提出的.在 1994 年 Agrawal 等人又提出了基于关联规则的 Apriori 算法,至今 Apr ...
- JAVA 实现 GET、POST、PUT、DELETE HTTP请求
1.get 2.put 3.post 4.delete
- Docker-Compose搭建单体SkyWalking
SkyWalking简介 SkyWalking是一款高效的分布式链路追踪框架,对于处理分布式的调用链路的问题定位上有很大帮助 有以下特点: 性能好 针对单实例5000tps的应用,在全量采集的情况下, ...
- WinForm控件之【Button】
基本介绍 普通按钮大部分情况下用作页面对某系列操作后的提交确认,应用较为广泛,在winfrom控件当中使用设置都相对的简单. 常设置属性.事件 Image:控件上显示的图片: Enabled :指示 ...
- map中存放list<实体类>解析
package com: import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Arra ...
- windbg 配置符号路径
(转)WINDBG的符号下载与符号路径问题 安装与配置 windbg 的 symbol (符号) 本篇是新手自己写的一点心得.建议新手看看.同时希望前辈多多指教. 写这篇的动机:在网上找了一上午的 w ...
- deque双端队列笔记
clear()clear()clear():清空队列 pushpushpush_back()back()back():从尾部插入一个元素. pushpushpush_front()front()fro ...
- koa2服务端使用jwt进行鉴权及路由权限分发
大体思路 后端书写REST api时,有一些api是非常敏感的,比如获取用户个人信息,查看所有用户列表,修改密码等.如果不对这些api进行保护,那么别人就可以很容易地获取并调用这些 api 进行操作. ...
- 个人永久性免费-Excel催化剂功能第85波-灵活便捷的批量发送短信功能(使用腾讯云接口)
微信时代的今天,短信一样不可缺席,大系统都有集成短信接口.若只是临时用一下,若能够直接在Excel上加工好内容就可以直接发送,这些假设在此篇批量群发短信功能中都为大家带来完美答案. 业务场景 不多说, ...