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

299 篇文章 5 订阅

  在程序开发过程当中,往往存在这样一种情况,程序首先执行完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就是能够实现这种思想的典型工具类。
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的示例 Java 代码,用于比较多数据串行并行执行时间的差异: ```java import java.util.ArrayList; import java.util.List; public class Main { // 定义一个计算函数,用于计算一个数的平方 public static int calculateSquare(int n) { try { Thread.sleep(1000); // 模拟计算过程 } catch (InterruptedException e) { e.printStackTrace(); } return n * n; } // 串行执行计算任务 public static void serialExecution() { long startTime = System.currentTimeMillis(); List<Integer> input = new ArrayList<>(); for (int i = 0; i < 10; i++) { input.add(i); } List<Integer> output = new ArrayList<>(); for (int i : input) { int result = calculateSquare(i); System.out.println("Result " + i + ": " + result); output.add(result); } long endTime = System.currentTimeMillis(); System.out.println("Serial execution time: " + (endTime - startTime) / 1000.0 + " seconds"); } // 并行执行计算任务 public static void parallelExecution() { long startTime = System.currentTimeMillis(); List<Integer> input = new ArrayList<>(); for (int i = 0; i < 10; i++) { input.add(i); } List<Integer> output = new ArrayList<>(); input.parallelStream().forEach(i -> { int result = calculateSquare(i); System.out.println("Result " + i + ": " + result); output.add(result); }); long endTime = System.currentTimeMillis(); System.out.println("Parallel execution time: " + (endTime - startTime) / 1000.0 + " seconds"); } public static void main(String[] args) { serialExecution(); parallelExecution(); } } ``` 在这个示例代码中,我们定义了一个 `calculateSquare` 函数,用于计算一个数的平方。我们用一个 `for` 循环来计算 0 到 9 的整数的平方,然后比较串行并行执行这个计算任务的时间。 在串行执行函数 `serialExecution` 中,我们按照顺序依次计算每个数的平方,因此总时间为 10 秒。 在并行执行函数 `parallelExecution` 中,我们使用了 Java 8 中的 `parallelStream` 方法来并行执行计算任务。我们将输入数据转换为一个流,并使用 `forEach` 方法来对每个数并行执行计算任务。因为我们使用了并行流来并行执行任务,因此总时间只需要 1 秒左右。 通过比较串行并行执行的时间,我们可以看到并行执行可以大大提高任务的执行效率。需要注意的是,并行执行需要一定的额外开销,因此在小规模数据处理时,串行执行可能更为高效。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值