前言问题:最近个人在做手机银行的资产负债项目的时候遇到了问题,就是,用户资产计算逻辑业务复杂繁多,各中心服务数据透传统计,有的服务不相关,使用串行的效果极为不理想,响应速度太慢,生产压测有的响应需要近一分钟
问题简介:现在有一个计算业务,需要将A方法的返回值+B方法的返回值+C方法的返回值,然而ABC三个互不相关,传统的串行方式为执行A获取返回值,执行B获取返回值,再执行C获取返回值,最后再将A、B、C的返回值做计算得到结果。那如果,A服务平均耗时8秒,B服务平均耗时5秒,C服务平均耗时3秒,那样串行就是耗时16秒左右,效果显然不理想。
优化方案:使用多线程的方式,ABC三个服务同时进行,总耗时就是A最多的8秒左右,传统的使用Thread或者Runable获取不到返回值,此处使用Future
首先模拟测试、定义一个服务类,里面有三个方法,方法一执行需5秒,方法二执行需3秒,方法三执行需2秒,方法二为含参方法
由于使用cglib,而强大的spring引入了,所以只要是spring项目直接使用即可
1.定义Task类实现Callable接口
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
/**
* @Classname Task
* @Date 2020/8/26 11:14
* @Created by gangye
*/
public class Task<T> implements Callable<T> {
private Object object;//指定对象
private Object[] args;//方法的参数
private String methodName;//方法名
public Task(Object object, String methodName, Object[] args) {
this.object = object;
this.args = args;
this.methodName = methodName;
}
public T call() throws Exception {
Class[] classes = new Class[0];
if (null!=args && args.length>0){
classes = new Class[args.length];
for (int i = 0; i<args.length; i++){
classes[i] = args[i].getClass();
}
}
Method method = object.getClass().getMethod(methodName, classes);
return (T) method.invoke(object, args);
}
}
2.定义并行类,里面试用延时加载内部类,此处延时加载类,是为了防止调用future.get()方法造成的阻塞,造成达不到并行效果,实际上又是串行,因为get获取方法的返回值
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.LazyLoader;
import java.util.concurrent.Future;
/**
* @Classname Parallel
* @Date 2020/8/26 11:04
* @Created by gangye
*/
public class Parallel {
/**
* 延迟加载类
* @param <T>
*/
class FutureLazyLoader<T> implements LazyLoader {
private Future<T> future;
public FutureLazyLoader(Future<T> future) {
this.future = future;
}
public Object loadObject() throws Exception {
return future.get();
}
}
public <T> T futureGetProxy(Future<T> future, Class clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
return (T) enhancer.create(clazz, new FutureLazyLoader(future));
}
}
3.定义方法提供的类,即此类中的方法是逻辑处理,各方法间互不相干,同时定义返回的类型,基本数据类型为final,故不可将final类型作为返回类型,因为上述代理方法中有enhancer.setSuperclass(clazz);方法
public class TestMethod {
public TempResult method1() {
TempResult tempResult = new TempResult();
tempResult.setCalc(3);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return tempResult;
}
public TempResult method2(Integer num1, String numStr) {
TempResult tempResult = new TempResult();
int result = 0;
try {
result = num1 + Integer.valueOf(numStr);
tempResult.setCalc(result);
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return tempResult;
}
public TempResult method3() {
TempResult tempResult = new TempResult();
tempResult.setCalc(1);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return tempResult;
}
}
public class TempResult {
private int calc;
public int getCalc() {
return calc;
}
public void setCalc(int calc) {
this.calc = calc;
}
@Override
public String toString() {
return "TempResult{" +
"calc=" + calc +
'}';
}
}
4.创建测试类,以及主方法调用三个逻辑处理方法
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* @Classname ParallelTest
* @Date 2020/8/26 11:10
* @Created by gangye
*/
public class ParallelTest {
public void test02() throws Exception {
Parallel parallel = new Parallel();
TestMethod testMethod = new TestMethod();
ExecutorService executorService = Executors.newFixedThreadPool(2);
long start = System.currentTimeMillis();
// 开启线程执行
Future<TempResult> future1 = executorService.submit(new Task(testMethod, "method1", null));
// 不阻塞,正常执行,result1是cglib的代理类,采用延迟加载,只有在使用的时候才调用方法进行赋值。
TempResult result1 = parallel.futureGetProxy(future1, TempResult.class);
// 开启线程执行
Object[] objects = new Object[2];
objects[0] = 1;
objects[1] = "4";
Future<TempResult> future2 = executorService.submit(new Task(testMethod, "method2", objects));
// 不阻塞,正常执行,result2是cglib的代理类,采用延迟加载,只有在使用的时候才调用方法进行赋值。
TempResult result2 = parallel.futureGetProxy(future2, TempResult.class);
Future<TempResult> future3 = executorService.submit(new Task(testMethod, "method3", null));
// 不阻塞,正常执行,result3是cglib的代理类,采用延迟加载,只有在使用的时候才调用方法进行赋值。
TempResult result3 = parallel.futureGetProxy(future3, TempResult.class);
// 这里要使用baseResult1和baseResult2
System.out.println("method1的返回" + result1 + "\nmethod2的返回" + result2 + "\nmethod3的返回" + result3);
long end = System.currentTimeMillis();
// 总耗时time = max(time1,time2)
System.out.println("总耗时:" + (end - start));
//计算汇总
System.out.println("三个方法汇总计算结果"+(result1.getCalc()+result2.getCalc()+result3.getCalc()));
executorService.shutdown();
}
public static void main(String[] args) throws Exception{
System.out.println(new SimpleDateFormat("HH:mm:ss SSS").format(new Date()));
new ParallelTest().test02();
System.out.println(new SimpleDateFormat("HH:mm:ss SSS").format(new Date()));
}
}
关键点,为了有参构造使用:
注意:此处使用到了反射去执行方法,定义方法时是什么类型的值就传什么类型,不要使用父类型去定义,若传递的值是ArrayList类型,方法定义一定要是ArrayList类型的,不要使用List去定义数据类型,不然会报没有此方法的异常
最后执行程序
查看结果耗时5秒左右,达到预期结果
最后,此处有点糙,将线程池关了,在实际项目中,可以维护线程池,参考这篇文章
https://www.jianshu.com/p/2f6f0e625f35
参考文章:https://blog.csdn.net/yangzl2008/article/details/50489583/
最后可以参考这篇文章,学习学习(也可应用于此处场景)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/12308.html