溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Future模式的多線程異步調(diào)用介紹

發(fā)布時(shí)間:2020-05-11 15:18:29 來源:億速云 閱讀:203 作者:Leah 欄目:編程語言

篇文章為大家?guī)碛嘘P(guān)Future模式的多線程異步調(diào)用的詳細(xì)介紹。大部分命令行知識(shí)點(diǎn)都是大家經(jīng)常用到的,為此分享給大家做個(gè)參考。一起跟隨小編過來看看吧。

一、什么是異步調(diào)用

當(dāng)我們調(diào)用一個(gè)函數(shù)的時(shí)候,如果這個(gè)函數(shù)的執(zhí)行過程是很耗時(shí)的,我們就必須要等待,但是我們有時(shí)候并不急著要這個(gè)函數(shù)返回的結(jié)果。因此,我們可以讓被調(diào)者立即返回,讓他在后臺(tái)慢慢的處理這個(gè)請(qǐng)求。對(duì)于調(diào)用者來說,則可以先處理一些其他事情,在真正需要數(shù)據(jù)的時(shí)候再去嘗試獲得需要的數(shù)據(jù)(這個(gè)真正需要數(shù)據(jù)的位置也就是上文提到的阻塞點(diǎn))。這也是Future模式的核心思想:異步調(diào)用。

到了這里,你可能會(huì)想CountDownLatch不是也可以實(shí)現(xiàn)類似的功能的嗎?也是可以讓耗時(shí)的任務(wù)通過子線程的方式去執(zhí)行,然后設(shè)置一個(gè)阻塞點(diǎn)等待返回的結(jié)果,情況貌似是這樣的!但有時(shí)發(fā)現(xiàn)CountDownLatch只知道子線程的完成情況是不夠的,如果在子線程完成后獲取其計(jì)算的結(jié)果,那CountDownLatch就有些捉襟見襯了,所以JDK提供的Future類,不僅可以在子線程完成后收集其結(jié)果,還可以設(shè)定子線程的超時(shí)時(shí)間,避免主任務(wù)一直等待。

看到這里,似乎恍然大悟了!CountDownLatch無法很好的洞察子線程執(zhí)行的結(jié)果,使用Future就可以完成這一操作,那么Future何方神圣!下邊我們就細(xì)細(xì)聊一下。

二、Future模式

雖然,F(xiàn)uture模式不會(huì)立即返回你需要的數(shù)據(jù),但是,他會(huì)返回一個(gè)契約 ,以后在使用到數(shù)據(jù)的時(shí)候就可以通過這個(gè)契約獲取到需要的數(shù)據(jù)。

Future模式的多線程異步調(diào)用介紹

上圖顯示的是一個(gè)串行程序調(diào)用的流程,可以看出當(dāng)有一個(gè)程序執(zhí)行的時(shí)候比較耗時(shí)的時(shí)候,其他程序必須等待該耗時(shí)操作的結(jié)束,這樣的話客戶端就必須一直等待,知道返回?cái)?shù)據(jù)才執(zhí)行其他的任務(wù)處理。

Future模式的多線程異步調(diào)用介紹

上圖展示的是Future模式流程圖,在廣義的Future模式中,雖然獲取數(shù)據(jù)是一個(gè)耗時(shí)的操作,但是服務(wù)程序不等數(shù)據(jù)完成就立即返回客戶端一個(gè)偽造的數(shù)據(jù)(就是上述說的“契約”),實(shí)現(xiàn)了Future模式的客戶端并不急于對(duì)其進(jìn)行處理,而是先去處理其他業(yè)務(wù),充分利用了等待的時(shí)間,這也是Future模式的核心所在,在完成了其他數(shù)據(jù)無關(guān)的任務(wù)之后,最后在使用返回比較慢的Future數(shù)據(jù)。這樣在整個(gè)調(diào)用的過程中就不會(huì)出現(xiàn)長時(shí)間的等待,充分利用時(shí)間,從而提高系統(tǒng)效率。

1、Future主要角色

Future模式的多線程異步調(diào)用介紹

2、Future的核心結(jié)構(gòu)圖如下:

Future模式的多線程異步調(diào)用介紹

上述的流程就是說:Data為核心接口,這是客戶端希望獲取的數(shù)據(jù),在Future模式中,這個(gè)Data接口有兩個(gè)重要的實(shí)現(xiàn),分別是:RealData和FutureData。RealData就是真實(shí)的數(shù)據(jù),F(xiàn)utureData他是用來提取RealData真是數(shù)據(jù)的接口實(shí)現(xiàn),用于立即返回得到的,他實(shí)際上是真實(shí)數(shù)據(jù)RealData的代理,封裝了獲取RealData的等待過程。

說了這些理論的東西,倒不如直接看代碼來的直接些,請(qǐng)看代碼!

三、Future模式的簡單實(shí)現(xiàn)

主要包含以下5個(gè)類,對(duì)應(yīng)著Future模式的主要角色:

Future模式的多線程異步調(diào)用介紹

1、Data接口

/**
 * 返回?cái)?shù)據(jù)的接口
 */
public interface Data {

    String getResult();
}

2、FutureData代碼

/**
 * Future數(shù)據(jù),構(gòu)造很快,但是是一個(gè)虛擬的數(shù)據(jù),需要裝配RealData
 */
public class FutureData implements Data {

    private RealData realData = null;
    private boolean isReady = false;

    private ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    @Override
    public String getResult() {
        while (!isReady) {
            try {
                lock.lock();
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        return realData.getResult();
    }

    public void setRealData(RealData realData) {
        lock.lock();
        if (isReady) {
            return;
        }
        this.realData = realData;
        isReady = true;
        condition.signal();
        lock.unlock();
    }
}

3、RealData代碼

public class RealData implements Data {

    private String result;

    public RealData(String param) {
        StringBuffer sb = new StringBuffer();
        sb.append(param);
        try {
            //模擬構(gòu)造真實(shí)數(shù)據(jù)的耗時(shí)操作
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        result = sb.toString();
    }

    @Override
    public String getResult() {
        return result;
    }
}

4、Client代碼

public class Client {

    public Data request(String param) {
        //立即返回FutureData
        FutureData futureData = new FutureData();
        //開啟ClientThread線程裝配RealData
        new Thread(() -> {
            {
                //裝配RealData
                RealData realData = new RealData(param);
                futureData.setRealData(realData);
            }
        }).start();
        return futureData;
    }
}

5、Main

/**
 * 系統(tǒng)啟動(dòng),調(diào)用Client發(fā)出請(qǐng)求
 */
public class Main {

    public static void main(String[] args) {
        Client client = new Client();
        Data data = client.request("Hello Future!");
        System.out.println("請(qǐng)求完畢!");

        try {
            //模擬處理其他業(yè)務(wù)
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("真實(shí)數(shù)據(jù):" + data.getResult());
    }
}

6、執(zhí)行結(jié)果:

Future模式的多線程異步調(diào)用介紹

四、JDK中的Future模式實(shí)現(xiàn)

上述實(shí)現(xiàn)了一個(gè)簡單的Future模式的實(shí)現(xiàn),因?yàn)檫@是一個(gè)很常用的模式,在JDK中也給我們提供了對(duì)應(yīng)的方法和接口,先看一下實(shí)例:

public class RealData implements Callable<String> {

    private String result;

    public RealData(String result) {
        this.result = result;
    }

    @Override
    public String call() throws Exception {
        StringBuffer sb = new StringBuffer();
        sb.append(result);
        //模擬耗時(shí)的構(gòu)造數(shù)據(jù)過程
        Thread.sleep(5000);
        return sb.toString();
    }
}

這里的RealData 實(shí)現(xiàn)了Callable接口,重寫了call方法,在call方法里邊實(shí)現(xiàn)了構(gòu)造真實(shí)數(shù)據(jù)耗時(shí)的操作。

public class FutureMain {

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        FutureTask<String> futureTask = new FutureTask<>(new RealData("Hello"));

        ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.execute(futureTask);

        System.out.println("請(qǐng)求完畢!");

        try {
            Thread.sleep(2000);
            System.out.println("這里經(jīng)過了一個(gè)2秒的操作!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("真實(shí)數(shù)據(jù):" + futureTask.get());
        executorService.shutdown();
    }
}

執(zhí)行結(jié)果:

Future模式的多線程異步調(diào)用介紹

上述代碼,通過:FutureTask<String> futureTask = new FutureTask<>(new RealData("Hello")); 這一行構(gòu)造了一個(gè)futureTask 對(duì)象,表示這個(gè)任務(wù)是有返回值的,返回類型為String,下邊看一下FutureTask的類圖關(guān)系:

Future模式的多線程異步調(diào)用介紹

FutureTask實(shí)現(xiàn)了RunnableFuture接口,RunnableFuture接口繼承了Future和Runnable接口。因?yàn)镽unnableFuture實(shí)現(xiàn)了Runnable接口,因此FutureTask可以提交給Executor進(jìn)行執(zhí)行,F(xiàn)utureTask有兩個(gè)構(gòu)造方法,如下:

構(gòu)造方法1,參數(shù)為Callable:

Future模式的多線程異步調(diào)用介紹

構(gòu)造方法2,參數(shù)為Runnable:

Future模式的多線程異步調(diào)用介紹

上述的第二個(gè)構(gòu)造方法,傳入的是Runnable接口的話,會(huì)通過Executors.callable()方法轉(zhuǎn)化為Callable,適配過程如下:

Future模式的多線程異步調(diào)用介紹

Future模式的多線程異步調(diào)用介紹

這里為什么要將Runnable轉(zhuǎn)化為Callable哪?首先看一下兩者之間的區(qū)別:

(1) Callable規(guī)定的方法是call(),Runnable規(guī)定的方法是run();

(2) Callable的任務(wù)執(zhí)行后可返回值,而Runnable的任務(wù)是不能返回值得;

(3) call()方法可以拋出異常,run()方法不可以;

(4) 運(yùn)行Callable任務(wù)可以拿到一個(gè)Future對(duì)象,F(xiàn)uture 表示異步計(jì)算的結(jié)果。

最關(guān)鍵的是第二點(diǎn),就是Callable具有返回值,而Runnable沒有返回值。Callable提供了檢查計(jì)算是否完成的方法,以等待計(jì)算的完成,并獲取計(jì)算的結(jié)果。

計(jì)算完成后只能使用 get 方法來獲取結(jié)果,如果線程沒有執(zhí)行完,F(xiàn)uture.get()方法可能會(huì)阻塞當(dāng)前線程的執(zhí)行;如果線程出現(xiàn)異常,F(xiàn)uture.get()會(huì)throws InterruptedException或者ExecutionException;如果線程已經(jīng)取消,會(huì)拋出CancellationException。取消由cancel 方法來執(zhí)行。isDone確定任務(wù)是正常完成還是被取消了。

一旦計(jì)算完成,就不能再取消計(jì)算。如果為了可取消性而使用 Future 但又不提供可用的結(jié)果,則可以聲明Future<?> 形式類型、并返回 null 作為底層任務(wù)的結(jié)果。

以上就是Future模式的多線程異步調(diào)用的匯總,內(nèi)容較為全面,而且我也相信有相當(dāng)?shù)囊恍┕ぞ呖赡苁俏覀內(nèi)粘9ぷ骺赡軙?huì)見到或用到的。通過這篇文章,希望你能收獲更多。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI