Java 多線程的設計模式之 Future

Future 設計模式核心原理圖:

這裏寫圖片描述

client端通過 FutureData 發送一個執行耗時操作的請求,FutureData 則直接返回一個回調接口的引用(Data 接口,用於返回獲取到的真正結果),然後在 FutureData 內部再另起一個線程去執行真正的耗時操作。當 client 端 執行了獲取結果的方法時,如果執行完成,則返回結果。如果還在執行中,則會進入線程等待狀態,一直等到執行完成進行線程喚醒之後才能拿到結果。

這個設計模式在 Java JDK 中已經被實現了,這裏先看一下自測代碼,從最裏層開始看:

自測 Future 模式實現

  • RealData 實現類
public class RealData {

    private String data;

    public RealData(String data) throws InterruptedException {
        System.out.println("模擬數據加載中。。。");
        TimeUnit.SECONDS.sleep(5);
        this.data = data;
    }

    public String getResult() {
        return data + " - real data";
    }
}

上面流程圖中標註的 RealData 也要實現 Data 接口,這裏自己實現的話,其實也是可以省略的,自己寫一個獲取方法,效果也是一樣的。這裏模擬的是執行耗時操作。

  • FutureData 實現類
public class FutureData implements Data {

    private boolean isReady;// 是否準備好了。 也就是耗時操作是否執行完成了。
    private RealData data;// 真實數據操作對象

    public synchronized void setRequest(RealData data) {
        if (isReady) {
            return;
        }
        this.data = data;
        isReady = true;
        notify();
    }

    @Override
    public synchronized String getResult() throws InterruptedException {
        if (!isReady) {
            System.out.println("等待數據返回中。。。");
            wait();
        }
        return data.getResult();
    }
}

這裏說一下,因爲 RealData 的耗時操作直接在構造方法中寫的,所以,當進入 setRequest 方法的時候,說明 RealData 已經實例化好了,也就是耗時操作已經執行完成了。這裏 FutureData 也就是 RealData 的一個代理,包裝着 RealData 的耗時操作過程。

wait() 和 notify() 方法 一定要配合 synchronize 關鍵字使用。
  • Client 端實現
public class Client {

    public Data setRequest(String requestString) {
        FutureData data = new FutureData();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("client run : " + System.currentTimeMillis());
                    data.setRequest(new RealData(requestString));// 當 RealData 的構造方法執行完成之後,纔會進入到 setRequest的方法中。
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        return data;
    }
}
  • 調用代碼
public class Main {
    /**
     * Futrue 設計模式,
     * client端 發送某個耗時請求 - >  FutrueData 代理對象 先返回一個 假對象,然後其內部開啓線程進行真實請求操作 -- >  RealData 真實處理對象,處理結束之後,回調數據給 代理對象,代理對象再進行通知 client端
     */

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        Client client = new Client();
        Data data = client.setRequest("this is test");
        System.out.println("main : " + System.currentTimeMillis());
        System.out.println(data.getResult()); // 會進入線程等待狀態。

    }
}

JDK 內部 Future 實現

上面也說了,這個設計模式 JDK 內部是已經實現了這個機制。 FutureTask 作爲我們的代理類,它實現了 Runnable 接口,本身就作爲一個獨立的線程執行,但是它沒有start 方法,必須配合線程池使用。因爲 FutureTask 的傳入對象是 Callable(接口) 類型,所以,我們的 RealData 類也需要實現 Callable 接口。真實數據最終會通過 Callable 接口的 call() 方法傳遞給 FutureTask 。

  • 實現代碼:
public class RealData_1 implements Callable<String> {

    private String data;

    public RealData_1(String data) {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("real data 1");
        this.data = data;
    }

    @Override
    public String call() throws Exception {
        System.out.println("real data 1 call()");
        return data;
    }
}
  • 調用方式:
 public static void main(String[] args) throws InterruptedException, ExecutionException {
//        Client client = new Client();
//        Data data = client.setRequest("this is test");
//        System.out.println("main : " + System.currentTimeMillis());
//        System.out.println(data.getResult());

        /**
         * Java jdk 內置 FutureTask 是一個線程類,必須使用配合線程池調用。
         */
        FutureTask futureTask = new FutureTask(new RealData_1("this is test jdk future"));
        ExecutorService exe = Executors.newFixedThreadPool(1);

        exe.submit(futureTask);

        System.out.println(futureTask.get());

    }

最後說一下線程池的 submit 方法和 execute 方法的區別:

  • submit 可以傳入實現 Callable接口的實例對象。
  • submit 方法有返回值。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章