java串行程序转化成并行执行

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

1、串行程序

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

[java]  view plain  copy
  1. import com.yang.domain.BaseResult;  
  2. import org.junit.Test;  
  3.   
  4. import java.util.concurrent.TimeUnit;  
  5.   
  6. /** 
  7.  * @Description: 
  8.  * @Author: yangzl2008 
  9.  * @Date: 2016/1/9 19:06 
  10.  */  
  11. public class Serial {  
  12.   
  13.     @Test  
  14.     public void test() {  
  15.   
  16.         long start = System.currentTimeMillis();  
  17.   
  18.         BaseResult baseResult1 = method1();// 耗时操作1,时间 time1  
  19.         BaseResult baseResult2 = method2();// 耗时操作2,时间 time2  
  20.   
  21.         long end = System.currentTimeMillis();  
  22.   
  23.         //总耗时 time = time1 + time2  
  24.         System.out.println("baseResult1 is " + baseResult1 + "\nbaseResult2 is " + baseResult2 + "\ntime cost is " + (end - start));  
  25.     }  
  26.   
  27.     private BaseResult method1() {  
  28.         BaseResult baseResult = new BaseResult();  
  29.         try {  
  30.             TimeUnit.SECONDS.sleep(1);  
  31.         } catch (InterruptedException e) {  
  32.             e.printStackTrace();  
  33.         }  
  34.   
  35.         baseResult.setCode(1);  
  36.         baseResult.setMsg("method1");  
  37.         return baseResult;  
  38.     }  
  39.   
  40.     private BaseResult method2() {  
  41.         BaseResult baseResult = new BaseResult();  
  42.         try {  
  43.             TimeUnit.SECONDS.sleep(1);  
  44.         } catch (InterruptedException e) {  
  45.             e.printStackTrace();  
  46.         }  
  47.   
  48.         baseResult.setCode(1);  
  49.         baseResult.setMsg("method2");  
  50.         return baseResult;  
  51.     }  
  52.   
  53. }  
执行结果:
[plain]  view plain  copy
  1. baseResult1 is BaseResult{code=1, msg='method1'}  
  2. baseResult2 is BaseResult{code=1, msg='method2'}  
  3. 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(线程开启时间)。于是我们得到了串行程序的过渡态。
[java]  view plain  copy
  1. import com.yang.domain.BaseResult;  
  2. import org.junit.Test;  
  3.   
  4. import java.lang.reflect.Method;  
  5. import java.util.concurrent.*;  
  6.   
  7. /** 
  8.  * @Description: 
  9.  * @Author: yangzl2008 
  10.  * @Date: 2016/1/9 19:13 
  11.  */  
  12. public class SerialCallable {  
  13.   
  14.     @Test  
  15.     public void test01() throws Exception {  
  16.         // 两个线程的线程池  
  17.         ExecutorService executorService = Executors.newFixedThreadPool(2);  
  18.   
  19.         long start = System.currentTimeMillis();  
  20.   
  21.         // 开启线程执行  
  22.         Future<BaseResult> future1 = executorService.submit(new Task(this"method1"null));  
  23.         // 阻塞,直到程序返回。耗时time1  
  24.         BaseResult baseResult1 = future1.get();  
  25.         // 开启线程执行  
  26.         Future<BaseResult> future2 = executorService.submit(new Task(this"method1"null));  
  27.         // 阻塞,直到程序返回。耗时time2  
  28.         BaseResult baseResult2 = future2.get();  
  29.   
  30.         long end = System.currentTimeMillis();  
  31.   
  32.         // 总耗时 time = time1 + time2 + time(线程执行耗时)  
  33.         System.out.println("baseResult1 is " + baseResult1 + "\nbaseResult2 is " + baseResult2 + "\ntime cost is " + (end - start));  
  34.     }  
  35.   
  36.     public BaseResult method1() {  
  37.         BaseResult baseResult = new BaseResult();  
  38.         try {  
  39.             TimeUnit.SECONDS.sleep(1);  
  40.         } catch (InterruptedException e) {  
  41.             e.printStackTrace();  
  42.         }  
  43.   
  44.         baseResult.setCode(1);  
  45.         baseResult.setMsg("method1");  
  46.         return baseResult;  
  47.     }  
  48.   
  49.     public BaseResult method2() {  
  50.         BaseResult baseResult = new BaseResult();  
  51.         try {  
  52.             TimeUnit.SECONDS.sleep(1);  
  53.         } catch (InterruptedException e) {  
  54.             e.printStackTrace();  
  55.         }  
  56.   
  57.         baseResult.setCode(1);  
  58.         baseResult.setMsg("method2");  
  59.         return baseResult;  
  60.     }  
  61.   
  62.     class Task<T> implements Callable<T> {  
  63.   
  64.         private Object object;  
  65.   
  66.         private Object[] args;  
  67.   
  68.         private String methodName;  
  69.   
  70.         public Task(Object object, String methodName, Object[] args) {  
  71.             this.object = object;  
  72.             this.args = args;  
  73.             this.methodName = methodName;  
  74.         }  
  75.   
  76.         @Override  
  77.         public T call() throws Exception {  
  78.             Method method = object.getClass().getMethod(methodName);  
  79.             return (T) method.invoke(object, args);  
  80.         }  
  81.     }  
  82.   
  83. }  
执行结果:
[plain]  view plain  copy
  1. baseResult1 is BaseResult{code=1, msg='method1'}  
  2. baseResult2 is BaseResult{code=1, msg='method1'}  
  3. 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的延迟加载类,将串行的程序并行化!
[java]  view plain  copy
  1. import com.yang.domain.BaseResult;  
  2. import net.sf.cglib.proxy.Enhancer;  
  3. import net.sf.cglib.proxy.LazyLoader;  
  4. import org.junit.Test;  
  5.   
  6. import java.lang.reflect.Method;  
  7. import java.util.concurrent.*;  
  8.   
  9. /** 
  10.  * @Description: 
  11.  * @Author: yangzl2008 
  12.  * @Date: 2016/1/9 19:39 
  13.  */  
  14. public class Parallel {  
  15.   
  16.     @Test  
  17.     public void test02() throws Exception {  
  18.         ExecutorService executorService = Executors.newFixedThreadPool(2);  
  19.         long start = System.currentTimeMillis();  
  20.   
  21.         // 开启线程执行  
  22.         Future<BaseResult> future1 = executorService.submit(new Task(this"method1"null));  
  23.         // 不阻塞,正常执行,baseResult1是cglib的代理类,采用延迟加载,只有在使用的时候才调用方法进行赋值。  
  24.         BaseResult baseResult1 = futureGetProxy(future1, BaseResult.class);  
  25.         // 开启线程执行  
  26.         Future<BaseResult> future2 = executorService.submit(new Task(this"method2"null));  
  27.         // 不阻塞,正常执行,baseResult1是cglib的代理类,采用延迟加载,只有在使用的时候才调用方法进行赋值。  
  28.         BaseResult baseResult2 = futureGetProxy(future2, BaseResult.class);  
  29.         // 这里要使用baseResult1和baseResult2  
  30.         System.out.println("baseResult1 is " + baseResult1 + "\nbaseResult2 is " + baseResult2);  
  31.         long end = System.currentTimeMillis();  
  32.         // 总耗时time = max(time1,time2)  
  33.         System.out.println("time cost is " + (end - start));  
  34.     }  
  35.   
  36.     private <T> T futureGetProxy(Future<T> future, Class clazz) {  
  37.         Enhancer enhancer = new Enhancer();  
  38.         enhancer.setSuperclass(clazz);  
  39.         return (T) enhancer.create(clazz, new FutureLazyLoader(future));  
  40.     }  
  41.   
  42.     /** 
  43.      * 延迟加载类 
  44.      * @param <T> 
  45.      */  
  46.     class FutureLazyLoader<T> implements LazyLoader {  
  47.   
  48.         private Future<T> future;  
  49.   
  50.         public FutureLazyLoader(Future<T> future) {  
  51.             this.future = future;  
  52.         }  
  53.   
  54.         @Override  
  55.         public Object loadObject() throws Exception {  
  56.             return future.get();  
  57.         }  
  58.     }  
  59.   
  60.     public BaseResult method1() {  
  61.         BaseResult baseResult = new BaseResult();  
  62.         try {  
  63.             TimeUnit.SECONDS.sleep(1);  
  64.         } catch (InterruptedException e) {  
  65.             e.printStackTrace();  
  66.         }  
  67.   
  68.         baseResult.setCode(1);  
  69.         baseResult.setMsg("method1");  
  70.         return baseResult;  
  71.     }  
  72.   
  73.     public BaseResult method2() {  
  74.         BaseResult baseResult = new BaseResult();  
  75.         try {  
  76.             TimeUnit.SECONDS.sleep(1);  
  77.         } catch (InterruptedException e) {  
  78.             e.printStackTrace();  
  79.         }  
  80.   
  81.         baseResult.setCode(1);  
  82.         baseResult.setMsg("method2");  
  83.         return baseResult;  
  84.     }  
  85.   
  86.     class Task<T> implements Callable<T> {  
  87.   
  88.         private Object object;  
  89.   
  90.         private Object[] args;  
  91.   
  92.         private String methodName;  
  93.   
  94.         public Task(Object object, String methodName, Object[] args) {  
  95.             this.object = object;  
  96.             this.args = args;  
  97.             this.methodName = methodName;  
  98.         }  
  99.   
  100.         @Override  
  101.         public T call() throws Exception {  
  102.             Method method = object.getClass().getMethod(methodName);  
  103.             return (T) method.invoke(object, args);  
  104.         }  
  105.     }  
  106.   
  107. }  
执行结果:
[plain]  view plain  copy
  1. baseResult1 is BaseResult{code=1, msg='method1'}  
  2. baseResult2 is BaseResult{code=1, msg='method2'}  
  3. time cost is 1057  

4、思考

        以上,将method1,method2分别开新线程执行,最终得到结果后,再继续执行的实现有多种,在J.U.C中的CountDownLatch和CyclicBarrier就是能够实现这种思想的典型工具类。

猜你喜欢

转载自blog.csdn.net/com_ma/article/details/80807127