基於JAVA的Promise模式實現

Promise模式是一種異步編程模式,即咱們能夠先開始一個任務,拿到這個任務的憑據而並不須要當即獲得這個任務的執行結果才繼續往下執行,咱們拿着這個憑證能夠在以後任何須要的時候去兌換結果。這篇文章主要介紹一種基於JAVA的Promise模式實現並結合一些例子。前端


原始實現java

爲了可以讓你們對這個模式有個印象,我舉個簡單點的例子,假如咱們正在作百度百科這個頁面,咱們須要給前端提供一個服務(下面的代碼咱們將結果打印出來來模擬),能夠根據id獲取百科條目的內容,具體的例子是咱們想要獲取某個明星的百度百科信息。而這個明星的信息有兩個相關內容可能須要調用別人的服務來獲取,一個是獲取明星相關的人物信息;另外一個是獲取這個明星相關的新聞。咱們假設這兩個服務分別位於不一樣的業務部門,並且因爲業務的複雜性,服務比較慢,我用下面的代碼來表示:git

/**
 * @author float.lu
 */
public class OldLongCallExample {
    public static void main (String ...s) {
        long start = System.nanoTime();
        //280ms
        String result1 = getRelatedRoles();
        //250ms
        String result2 = getRelatedNews();
        System.out.println("Result:" + result1 + result2);
        System.out.println("take:" + TimeUnit.NANOSECONDS.toMillis((System.nanoTime() - start)) + "ms");
    }

上面的代碼咱們能夠看出,咱們首先調用getRelatedRoles獲取明星相關人物,這裏須要花費280ms,拿到明星相關人物以後咱們繼續調用getRelatedNew獲取明星相關新聞,這裏須要250ms,咱們總共須要約530ms,這個時間是很長的。編程


思考promise

其實對於上面的這種狀況,咱們在調用getRelatedRoles以後其實並不須要等待getRelatedRoles給出結果以後在往下進行,由於後面花費時間較長的部分getRelatedNews並不依賴於getRelatedRoles的結果,所以咱們順利成章的認爲咱們能夠在調用getRelatedRoles以後不須要等待,咱們繼續調用getRelatedNews,當主方法須要返回的時候咱們再分別取拿getRelatedRoles和getRelatedNews的結果,這就是本文基於Promise模式的實現要解決的事情。異步


基於JPromise的實現ide

JPromise是基於JAVA的Promise的實現,在抽象類Promise中咱們主要提供了三個抽象方法:異步編程

/**
 * 獲取執行結果
 * @return
 */
public abstract V get();

/**
 * 帶超時時間的獲取執行結果
 * @param timeout
 * @param unit
 * @return
 */
public abstract V get(long timeout, TimeUnit unit);

/**
 * 獲取異常信息
 * @return
 */
public abstract Throwable getException();

和一個將普通代碼邏輯包裝成Promise的工具方法:工具

/**
 * 將普通邏輯包裝成Promise
 * @param task
 * @param <V>
 * @return
 */
public static <V> Promise<V> wrap(final AsyncTask<V> task){
    if (executorService == null) {
        setDefaultExecutorService();
    }
    return new DefaultPromise<V>(executorService.submit(new Callable<V>() {
        @Override
        public V call() throws Exception {
            return task.call();
        }
    }));
}

咱們經過JPromise能夠將不一樣的業務邏輯包裝成Promise對象,咱們把Promise對象稱做憑證,經過Promise抽象類提供的get或get(timeout,unit)方法來在須要的時候根據憑證獲取結果,同時容許超時。那麼最開始的實現能夠改形成下面的樣子:測試

/**
 * @author float.lu
 */
public class PromiseLongCallExample {
    public static void main (String ...s) {
        long start = System.nanoTime();
        //提交任務不須要當即拿到結果
        Promise<String> resolve1 = Promise.wrap(new AsyncTask<String>() {
            @Override
            public String call() throws Exception {
                return getRelatedRoles();
            }
        });
        //提交任務不須要當即拿到結果
        Promise<String> resolve2 = Promise.wrap(new AsyncTask<String>() {
            @Override
            public String call() throws Exception {
                return getRelatedNews();
            }
        });

        System.out.println("Result:" + resolve1.get() + resolve2.get());
        System.out.println("take:" + TimeUnit.NANOSECONDS.toMillis((System.nanoTime() - start)) + "ms");
    }

通過測試,上面的代碼執行時間約爲280ms,相比最開始的530ms獲得了很大的優化。


異常捕獲

一般異步執行的邏輯都會面臨着異常捕獲的問題,Jpromise的實現將邏輯異常捕獲並經過getException向外面提供訪問接口。


自定義線程池

基於JPromise的異步任務都是由額外的線程執行的,所以咱們須要爲JPromise配置線程池。JPromise支持用戶自定義線程池,包括基於Spring容器的配置,只須要將自定義的ExecutorService注入到PromiseFigure的executorService屬性便可。


總結

Promise模式的適用場景也是有限的,當下層邏輯不依賴於上層邏輯,這些邏輯一般是串行執行的時候可使用Promise模式來進行優化,優化效果仍是很顯著的。


項目地址:https://git.oschina.net/floatlu/jpromise

相關文章
相關標籤/搜索