使用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 ...
随机推荐
- html、jsp页面标签的遍历
应用场景:最近的项目中二级子页面遍历生成.操作表格比较多,记录一下一直用的遍历方法. 一般此类表格都是通过ajax请求数据,然后从callbackFunction中获取数据集合,遍历生成表: eg: ...
- python 如何在某.py文件中调用其他.py内的函数
A.py的文件需要调用B.py文件内的test函数 同一目录下: A.py #!/usr/bin/env python # -*- coding: utf- -*- def test(): ''' 测 ...
- 前端摸爬滚打之路(一)之 JavaScript 基础
前言:这是我第一次在博客上记录自己的前端学习过程,以往都是在桌面右侧开个 onenote 小窗,记录自己在学习过程中获得的知识.通常都是记录的满满当当,然后心满意足的关闭窗口,但是记录不代表学会.这些 ...
- Python基础-组织列表
1.字符串的几个常用方法 name = ''adb lovelace '' name.title() 首字母大写 name.upper() 全部大写 name.lower() 全部小写 2.组织列表操 ...
- mysql-8.0.16-winx64/Linux修改root用户密码
连接数据库等基础操作请自行解决哈,本篇是重点记录如何改密码. 一.查询用户密码: 查询用户密码命令: select host, user, authentication_string from mys ...
- SSM集合
SSM集成 1. Spring和各个框架的整合 Spring目前是JavaWeb开发中最终的框架,提供一站式服务,可以其他各个框架整合集成 Spring整合方案 1.1. SSH ssh是早期的一 ...
- 利用LDAP操作AD域
LDAP操作代码样例 初始化LDAP 目录服务上下文 该例子中,我们使用uid=linly,ou=People,dc=jsoso,dc=net这个账号,链接位于本机8389端口的LDAP服务器(ld ...
- !干货! 为设计指定输入驱动强度 set_driving_cell set_drive set_input_transition
!干货! 为设计指定输入驱动强度 set_driving_cell set_drive set_input_transition 本文转自:自己的微信公众号<集成电路设计及EDA教程> 里 ...
- 嵊州D2T2 八月惊魂 全排列 next_permutation()
嵊州D2T2 八月惊魂 这是一个远古时期的秘密,至今已无人关心. 这个世界的每个时代可以和一个 1 ∼ n 的排列一一对应. 时代越早,所对应的排列字典序就越小. 我们知道,公爵已经是 m 个时代前的 ...
- Elasticsearch实战总结
上手elasticsearch有段时间了,主要以应用为主,未做深入的研究,下面就简单的日常作个简单的总结,做个记录. 版本问题 es版本繁杂,让首次使用的人无从下手.常见的有2+.5+版本,最新版已达 ...