多线程将多个互不相关的服务串行优化为并行

前言问题:最近个人在做手机银行的资产负债项目的时候遇到了问题,就是,用户资产计算逻辑业务复杂繁多,各中心服务数据透传统计,有的服务不相关,使用串行的效果极为不理想,响应速度太慢,生产压测有的响应需要近一分钟

问题简介:现在有一个计算业务,需要将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://blog.csdn.net/qq_42684642/article/details/104985137

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值