一种Furture模式处理请求中循环独立的任务的方法
2018-01-11 10:58
363 查看
业务中经常碰到查询一个list后又需要对list进行后续处理(在sql层面不方便处理),需要循环遍历list
如果list中各item的业务独立,可用future模式来大大提高性能
1.关于future模式的概念
参考:彻底理解Java的Future模式 https://www.cnblogs.com/cz123/p/7693064.html
先上一个场景:假如你突然想做饭,但是没有厨具,也没有食材。网上购买厨具比较方便,食材去超市买更放心。实现分析:在快递员送厨具的期间,我们肯定不会闲着,可以去超市买食材。所以,在主线程里面另起一个子线程去网购厨具。但是,子线程执行的结果是要返回厨具的,而run方法是没有返回值的。所以,这才是难点,需要好好考虑一下。
2.关于实现
(1) callable+future+线程池:
Future<Integer> future =es.submit(calTask);
callable无法直接start,需要借助线程池
Future<T> = 线程池.submit(Callable<T>);
Future<T>.get();
(2) callable +futuretask+线程池
FutureTask<Integer> futureTask=new FutureTask<>(calTask);
//执行任务
es.submit(futureTask);
FutureTask<T> = new FutureTask<T>(Callable<T>);
线程池.submit(FutureTask<T>)
FutureTask<T>.get();
(3) callable +futuretask+Thread
同时FureTask也实现了runnable,可以直接允许不用线程池
FutureTask<Class> ft = new FutureTask<Class>((Callable<Class>)
Thread thread = new Thread(ft);
thread.start;
FutureTask<T> = new FutureTask<T>(Callable<T>);
Thread = new Thread(FutureTask<T>);
Thread.start();
FutureTask<T>.get();
参考:Java多线程编程:Callable、Future和FutureTask浅析(多线程编程之四) 博客比较深入 http://blog.csdn.net/javazejian/article/details/50896505
3. 应用
业务模型是这样的:
List<T> = sql;
for(T : list<T>) {
sql(T);
T.doSomething();
}
以future模式重构List<T> = sql;
List<FutureTask<T>> taskList = new ArrayList<FutureTask<T>>(list.size());
for(T t: list<T>) {
FutureTask<T> f = new FutureTask<T>(new TaskCallable<T>(t));
taskList.add(f);
Thread thread = new Thread(f);
thread.start();
}
for(FutureTask<T> ft : taskList) {
ft.get();
}
将T分为多个线程并行处理,同时等待所有计算结果,整合后返回这种模式在业务中达到以下效果:
另一个请求:
可以看到,微观上响应速度提高了5倍
以腾讯压力测试:
分析:20并发下,性能差不多差一倍
100并发下,差距不大了,推测为mysql顶不住了,证明这种方案在高压下不可取,原本一次查询变成了n+1次,应尽量避免
4.封装
虽然原则上予以避免,力求在单次sql层面解决,但迫于需求频繁修改,由于这种情况比较常见,故封装一下:
@Service
public class FurtureService<T> {
/**
*
* @param o 外部类对象,用于取得某对象的内部类
* @param list 待循环多线程处理的数据
* @param c 内部Callable类型
* @param args 内部类参数 数组
*/
public void run(Object o, List<T> list, Class c, Object [] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, ExecutionException, InterruptedException {
List<FutureTask<Class>> taskList = new ArrayList<FutureTask<Class>>(list.size());
// ExecutorService exec = Executors.newFixedThreadPool(list.size());
Constructor [] constructor = null;
constructor = c.getConstructors();
for(int i=0; i<list.size(); ++i) {
T t = list.get(i);
FutureTask<Class> ft = new FutureTask<Class>((Callable<Class>) constructor[0].newInstance(o, list.get(i), args));
taskList.add(ft);
// exec.submit(ft);
Thread thread = new Thread(ft);
thread.start();
}
for (FutureTask<Class> ft : taskList) {
ft.get();
}
// exec.shutdown();
}
}
调用: @Autowired
private FurtureService<T> furtureService;
1.提高了cpu使用率
2.一定程度弥补了不能在数据库层面一并取出数据的性能
缺点:
1.数据库压力倍增,1次sql变成了n+1次sql查询
355308311275271
205178119105849196
455422422450517480
2501791521201029288
如果list中各item的业务独立,可用future模式来大大提高性能
1.关于future模式的概念
参考:彻底理解Java的Future模式 https://www.cnblogs.com/cz123/p/7693064.html
先上一个场景:假如你突然想做饭,但是没有厨具,也没有食材。网上购买厨具比较方便,食材去超市买更放心。实现分析:在快递员送厨具的期间,我们肯定不会闲着,可以去超市买食材。所以,在主线程里面另起一个子线程去网购厨具。但是,子线程执行的结果是要返回厨具的,而run方法是没有返回值的。所以,这才是难点,需要好好考虑一下。
2.关于实现
(1) callable+future+线程池:
Future<Integer> future =es.submit(calTask);
callable无法直接start,需要借助线程池
Future<T> = 线程池.submit(Callable<T>);
Future<T>.get();
(2) callable +futuretask+线程池
FutureTask<Integer> futureTask=new FutureTask<>(calTask);
//执行任务
es.submit(futureTask);
FutureTask<T> = new FutureTask<T>(Callable<T>);
线程池.submit(FutureTask<T>)
FutureTask<T>.get();
(3) callable +futuretask+Thread
同时FureTask也实现了runnable,可以直接允许不用线程池
FutureTask<Class> ft = new FutureTask<Class>((Callable<Class>)
Thread thread = new Thread(ft);
thread.start;
FutureTask<T> = new FutureTask<T>(Callable<T>);
Thread = new Thread(FutureTask<T>);
Thread.start();
FutureTask<T>.get();
参考:Java多线程编程:Callable、Future和FutureTask浅析(多线程编程之四) 博客比较深入 http://blog.csdn.net/javazejian/article/details/50896505
3. 应用
业务模型是这样的:
List<T> = sql;
for(T : list<T>) {
sql(T);
T.doSomething();
}
以future模式重构List<T> = sql;
List<FutureTask<T>> taskList = new ArrayList<FutureTask<T>>(list.size());
for(T t: list<T>) {
FutureTask<T> f = new FutureTask<T>(new TaskCallable<T>(t));
taskList.add(f);
Thread thread = new Thread(f);
thread.start();
}
for(FutureTask<T> ft : taskList) {
ft.get();
}
将T分为多个线程并行处理,同时等待所有计算结果,整合后返回这种模式在业务中达到以下效果:
原(ms) | 现(ms) |
455 | 179 |
422 | 152 |
422 | 120 |
450 | 102 |
517 | 92 |
480 | 88 |
原 A(ms) | 现 B(ms) |
1217 | 216 |
584 | 184 |
505 | 171 |
503 | 116 |
556 | 96 |
线上60ms | 22ms |
以腾讯压力测试:
A | B | |
20并发(4*4)2分钟 第一次 | tps:127.18 97.15ms | tps:212.98 57.57ms |
第二次 | tps:123.59 100.57ms | tps:208.77 57.50ms |
100并发 唯一一次 | tps:241.39 233.02ms | tps:256.48 209.21ms |
不处理 唯一一次 tps:793.77 71.93ms | ||
100并发下,差距不大了,推测为mysql顶不住了,证明这种方案在高压下不可取,原本一次查询变成了n+1次,应尽量避免
4.封装
虽然原则上予以避免,力求在单次sql层面解决,但迫于需求频繁修改,由于这种情况比较常见,故封装一下:
@Service
public class FurtureService<T> {
/**
*
* @param o 外部类对象,用于取得某对象的内部类
* @param list 待循环多线程处理的数据
* @param c 内部Callable类型
* @param args 内部类参数 数组
*/
public void run(Object o, List<T> list, Class c, Object [] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, ExecutionException, InterruptedException {
List<FutureTask<Class>> taskList = new ArrayList<FutureTask<Class>>(list.size());
// ExecutorService exec = Executors.newFixedThreadPool(list.size());
Constructor [] constructor = null;
constructor = c.getConstructors();
for(int i=0; i<list.size(); ++i) {
T t = list.get(i);
FutureTask<Class> ft = new FutureTask<Class>((Callable<Class>) constructor[0].newInstance(o, list.get(i), args));
taskList.add(ft);
// exec.submit(ft);
Thread thread = new Thread(ft);
thread.start();
}
for (FutureTask<Class> ft : taskList) {
ft.get();
}
// exec.shutdown();
}
}
调用: @Autowired
private FurtureService<T> furtureService;
furtureService.run(this, list, ComputeTask.class, new Object[]{para});其实还是有优点的:
1.提高了cpu使用率
2.一定程度弥补了不能在数据库层面一并取出数据的性能
缺点:
1.数据库压力倍增,1次sql变成了n+1次sql查询
205178119105849196
455422422450517480
2501791521201029288
相关文章推荐
- Http请求超时的一种处理方法
- C++第六周任务一【任务1】下面的程序存在编译错误。有两种方法可以修改,请给出这两种修改方案,在报告中说明你倾向于用哪一种?为什么?处理此类问题的原则是什么?
- 一种异步作业处理请求任务的简单实现(以短信发送为例)
- uC/OS-II任务栈处理的一种改进方法
- uC/OS-II任务栈处理的一种改进方法
- 一种MFC误报动态库中内存泄露的处理方法
- angularjs 处理多个异步请求方法汇总
- TCP Server处理多Client请求的方法—非阻塞accept与select
- Objective-C中一种消息处理方法performSelector
- 心得11--案例分析request.getparameter()方法的用法及请求乱码处理、分配器方法
- hdu3851(dp+区间范围很大且有循环的处理方法)
- Python使用 Beanstalkd 做异步任务处理的方法
- IE下window.onresize 多次调用与死循环bug处理方法介绍
- outlook 处理请求的任务时出错,请复查下列错误表以获取详细信息。
- FIFO 非阻塞写+非阻塞读+延时循环读的一种方法
- 处理同时进行多个网络请求的方法
- Gearman分布式任务处理系统(五)版本介绍、安装方法和使用说明
- JQurey中的getJSON方法请求的接口有错误时的处理方法
- 一种异常处理的方法、装置和软件系统
- node中的定时任务处理方法