Java串列程式並行化執行__Java

來源:互聯網
上載者:User

        在程式開發過程當中,往往存在這樣一種情況,程式首先執行完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、參考 實戰CGLib系列之proxy篇(三):消極式載入LazyLoader 實戰cglib 實現AOP — CGLIB

程式源碼下載


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.