Java串行程序并行化执行

        在程序开发过程当中,往往存在这样一种情况,程序首先执行完method1得到结果result1之后,在执行method2获得结果result2,然后再按照result1和result2的结果来判定程序下一步的执行。在这里method1和method2是相互不关联的,即method1的执行和method2的执行位置可以调整,而不影响程序的执行结果。

1、串行程序

        传统意义上的写法,我们得到的往往会是串行执行的程序形态,程序的总的执行时间是method1的执行时间time1加上method2的执行时间time2,这样总的执行时间time=time1+time2。我们得到的是串行的程序形态。

import com.yang.domain.BaseResult;
import org.junit.Test;

import java.util.concurrent.TimeUnit;

/**
 * @Description:
 * @Author: yangzl2008
 * @Date: 2016/1/9 19:06
 */
public class Serial {

    @Test
    public void test() {

        long start = System.currentTimeMillis();

        BaseResult baseResult1 = method1();// 耗时操作1,时间 time1
        BaseResult baseResult2 = method2();// 耗时操作2,时间 time2

        long end = System.currentTimeMillis();

        //总耗时 time = time1 + time2
        System.out.println("baseResult1 is " + baseResult1 + "\nbaseResult2 is " + baseResult2 + "\ntime cost is " + (end - start));
    }

    private BaseResult method1() {
        BaseResult baseResult = new BaseResult();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        baseResult.setCode(1);
        baseResult.setMsg("method1");
        return baseResult;
    }

    private BaseResult method2() {
        BaseResult baseResult = new BaseResult();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        baseResult.setCode(1);
        baseResult.setMsg("method2");
        return baseResult;
    }

}
执行结果:
baseResult1 is BaseResult{code=1, msg='method1'}
baseResult2 is BaseResult{code=1, msg='method2'}
time cost is 2000

2、串行程序的多线程过渡

          而这种代码是不是可优化的地方呢?加快程序的执行效率,降低程序的执行时间。在这里method1和method2是相互不关联的,即method1的执行和method2的执行位置可以调整,而不影响程序的执行结果,我们可不可以为建立线程1执行method1然后建立线程2来执行method2呢,因为method1和method2都需要得到结果,因此我们需要使用Callable接口,然后使用Future.get()得到执行的结果,但实际上Future.get()在程序返回结果之前是阻塞的,即,线程1在执行method1方式时,程序因为调用了Future.get()会等待在这里直到method1返回结果result1,然后线程2才能执行method2,同样,Future.get()也会一直等待直到method2的结果result2返回,这样,我们开启了线程1,开启了线程2并没有得到并发执行method1,method2的效果,反而会因为程序开启线程而多占用了程序的执行时间,这样程序的执行时间time=time1+time2+time(线程开启时间)。于是我们得到了串行程序的过渡态。
import com.yang.domain.BaseResult;
import org.junit.Test;

import java.lang.reflect.Method;
import java.util.concurrent.*;

/**
 * @Description:
 * @Author: yangzl2008
 * @Date: 2016/1/9 19:13
 */
public class SerialCallable {

    @Test
    public void test01() throws Exception {
        // 两个线程的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        long start = System.currentTimeMillis();

        // 开启线程执行
        Future<BaseResult> future1 = executorService.submit(new Task(this, "method1", null));
        // 阻塞,直到程序返回。耗时time1
        BaseResult baseResult1 = future1.get();
        // 开启线程执行
        Future<BaseResult> future2 = executorService.submit(new Task(this, "method1", null));
        // 阻塞,直到程序返回。耗时time2
        BaseResult baseResult2 = future2.get();

        long end = System.currentTimeMillis();

        // 总耗时 time = time1 + time2 + time(线程执行耗时)
        System.out.println("baseResult1 is " + baseResult1 + "\nbaseResult2 is " + baseResult2 + "\ntime cost is " + (end - start));
    }

    public BaseResult method1() {
        BaseResult baseResult = new BaseResult();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        baseResult.setCode(1);
        baseResult.setMsg("method1");
        return baseResult;
    }

    public BaseResult method2() {
        BaseResult baseResult = new BaseResult();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        baseResult.setCode(1);
        baseResult.setMsg("method2");
        return baseResult;
    }

    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;
        }

        @Override
        public T call() throws Exception {
            Method method = object.getClass().getMethod(methodName);
            return (T) method.invoke(object, args);
        }
    }

}
执行结果:
baseResult1 is BaseResult{code=1, msg='method1'}
baseResult2 is BaseResult{code=1, msg='method1'}
time cost is 2001

3、并行程序

        有没有方法可以在执行method1的时候同时执行method2,最后得到结果再进行处理?我们回到问题的出处,程序首先执行完method1得到结果result1之后,在执行method2获得结果result2,然后再按照result1和result2的结果来判定程序下一步的执行,最终我们得到的结果是result1和result2,然后再进行下一步操作,那么在我们得到result1和result2的时候,method1和method2其实是可以并发执行的,即我首先执行method1然后再执行mothod2,我不管他们的返回结果,只有在我要拿result1和result2进行操作的时候,程序才会调用Future.get()方法(这个方法会一直等待,直到结果返回),这是一种 延迟加载的思想, 与Hibernate中属性的延迟加载是一致的,即对于属性A,平时我是不用时不会进行赋值,只有我在用的时候,才执行SQL查询对其进行赋值操作。于是,我们得到了并发执行的程序形态。
        Hibernate亦使用CGLIB来实现延迟加载,因此,我们可以考虑使用CGLIB的延迟加载类,将串行的程序并行化!
import com.yang.domain.BaseResult;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.LazyLoader;
import org.junit.Test;

import java.lang.reflect.Method;
import java.util.concurrent.*;

/**
 * @Description:
 * @Author: yangzl2008
 * @Date: 2016/1/9 19:39
 */
public class Parallel {

    @Test
    public void test02() throws Exception {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        long start = System.currentTimeMillis();

        // 开启线程执行
        Future<BaseResult> future1 = executorService.submit(new Task(this, "method1", null));
        // 不阻塞,正常执行,baseResult1是cglib的代理类,采用延迟加载,只有在使用的时候才调用方法进行赋值。
        BaseResult baseResult1 = futureGetProxy(future1, BaseResult.class);
        // 开启线程执行
        Future<BaseResult> future2 = executorService.submit(new Task(this, "method2", null));
        // 不阻塞,正常执行,baseResult1是cglib的代理类,采用延迟加载,只有在使用的时候才调用方法进行赋值。
        BaseResult baseResult2 = futureGetProxy(future2, BaseResult.class);
        // 这里要使用baseResult1和baseResult2
        System.out.println("baseResult1 is " + baseResult1 + "\nbaseResult2 is " + baseResult2);
        long end = System.currentTimeMillis();
        // 总耗时time = max(time1,time2)
        System.out.println("time cost is " + (end - start));
    }

    private <T> T futureGetProxy(Future<T> future, Class clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        return (T) enhancer.create(clazz, new FutureLazyLoader(future));
    }

    /**
     * 延迟加载类
     * @param <T>
     */
    class FutureLazyLoader<T> implements LazyLoader {

        private Future<T> future;

        public FutureLazyLoader(Future<T> future) {
            this.future = future;
        }

        @Override
        public Object loadObject() throws Exception {
            return future.get();
        }
    }

    public BaseResult method1() {
        BaseResult baseResult = new BaseResult();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        baseResult.setCode(1);
        baseResult.setMsg("method1");
        return baseResult;
    }

    public BaseResult method2() {
        BaseResult baseResult = new BaseResult();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        baseResult.setCode(1);
        baseResult.setMsg("method2");
        return baseResult;
    }

    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;
        }

        @Override
        public T call() throws Exception {
            Method method = object.getClass().getMethod(methodName);
            return (T) method.invoke(object, args);
        }
    }

}
执行结果:
baseResult1 is BaseResult{code=1, msg='method1'}
baseResult2 is BaseResult{code=1, msg='method2'}
time cost is 1057

4、思考

        以上,将method1,method2分别开新线程执行,最终得到结果后,再继续执行的实现有多种,在J.U.C中的CountDownLatch和CyclicBarrier就是能够实现这种思想的典型工具类。 本文只是在另外想出一种实现方案而已,但对于已经成型的程序,即不改变程序代码的情况下,使用AOP,CGLIB等实现现有程序的并行化,本文思路更容易实现。使用Spring的AOP实现方案,在切面方法中开启新线Callable程执行被代理的方法,同时使用CGLIB对返回值进行延迟加载。

5、参考


猜你喜欢

转载自blog.csdn.net/yangzl2008/article/details/50489583