[面試]future模式

Future模式

什麼是future模式?

傳統單線程環境下,調用函數是同步的,必須等待程序返回結果後,纔可進行其餘處理。 Futrue模式下,調用方式改成異步。java

Futrue模式的核心在於:充分利用主函數中的等待時間,利用等待時間處理其餘任務,充分利用計算機資源。異步

簡單描述一下future模式的實現

future模式有兩種數據, 一種是真實數據RealData, 裏面就是業務中想要獲得的目標數據. 另外一種是虛擬數據FutureData, 它是在使用future模式時當即返回的一個對象. ide

調用方會首先拿到一個FutureData, 而後調用方就認爲本身拿到該數據了, 沒有進行阻塞, 繼續去執行下面的邏輯處理. 若是真實數據準備好了, 就會把本身的引用賦給以前的那個FutureData, 而且置一個標記, 表示這個FutureData裏面包含一個RealData, 拿到了想要的數據, 可使用.函數

詳細分的話, 會有下面這幾種狀況(假設RealData須要2秒才能建立好):this

1. 調用方發送了本身須要RealData的請求的後, 會當即拿到一個FutureData, 可是根本就不着急使用, 因此, 第2秒的時候RealData建立完成後, 就會被綁定到對應的FutureData裏. 假設第6秒調用方纔開始使用RealData, 他會發現FutureData已經準保好了他想要的數據, 因而開心地使用就ok了.線程

2. 調用方發送了本身須要RealData的請求的後, 會當即拿到一個FutureData, 可是很着急使用, 由於接下來的處理過程依賴於RealData的內容. 因而在第0.5秒的時候, 調用方就想要獲取RealData. 可是這個時候RealData並無準備好, 此時的FutureData是一個空殼而已. 因此就在這裏進行wait(或者忙等待). 直到RealData準備好,也就是再過1.5秒, 線程纔會喚醒(或者打破忙等待).對象

請用代碼實現一下Future模式

FutureData和RealData的統一抽象接口, Data類以下:

public interface Data {
    int getResult() throws InterruptedException;
}

RealData類

public class RealData implements Data {
    private int data;

    public RealData(int num) {
        //這裏用sleep來模擬構造一個複雜對象的場景
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        this.data = num * 10;
    }

    @Override
    public int getResult() {
        return data;
    }
}

FutureData類

public class FutureData implements Data {
    // 真實數據RealData的引用.
    private RealData realData = null;

    public synchronized void setRealData(RealData realData) {
        // 若是this.realData不是空, 說明已經準備好了, 直接return
        if (this.realData != null)
            return;
        this.realData = realData;
        notifyAll();
    }

    @Override
    public synchronized int getResult() throws InterruptedException {
        // 若是this.realData是null, 說明數據還沒準備好, 應該等待
        if (this.realData == null) {
            wait();
        }
        return realData.getResult();
    }
}

Client類

直接建立一個FutureData, 而後直接返回這個FutureData. 同事開闢一個線程來建立RealData, 而且在RealData建立完後綁定在FutureData中.blog

public class Client {
    public Data request(final int num) {
        // 當有請求的時候, 先建立一個虛擬對象.
        final FutureData futureData = new FutureData();

        // 而後開啓一個新線程去建立RealData, 當RealData建立完成後, 綁定帶FutureData裏.
        new Thread(() -> {
            RealData realData = new RealData(num);
            futureData.setRealData(realData);
        }).start();

        // 無論RealData有沒有建立完成, 都會直接返回這個FutureData.
        return futureData;
    }
}

Main

調用這個Future模型.接口

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Client client = new Client();

        // 調用了以後會當即返回一個FutureData, 這個data就是FutureData
        Data data = client.request(4);

        // 用sleep來模擬主線程正在處理其餘事情
        Thread.sleep(0);

        // getResult來獲取真實數據
        //     |- 若是這時候真實數據沒準備好, 那麼就wait, 等待notify, 而後獲取到真實數據
        //     |- 若是這時候真實數據準備好了, 那麼就能夠直接獲取到了
        System.out.println("數據=" + data.getResult());
    }
}

 在RealData處進行了*10 的處理, 因此request(4), 最終會返回40.資源

使用過JDK自帶的Future模式嗎?

使用過, 例子以下:

定義一個RealData類

import java.util.concurrent.Callable;

public class RealData implements Callable<Integer> {
    private int data;

    public RealData(int data) {
        this.data = data * 10;
    }

    @Override
    public Integer call() {
        //利用sleep方法來表示真是業務是很是緩慢的
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return data;
    }
}

 Main

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

public class Main {
    public static void main(String[] args) throws Exception {
        //線程池
        ExecutorService executor = Executors.newFixedThreadPool(1); //使用線程池

        // 以前本身實現的future模式中的 Data data = client.request(4) 這句至關於下面這兩行代碼
        //1. Data data
        FutureTask<Integer> futureTask = new FutureTask<>(new RealData(4));
        //2. 這裏至關於 client.request(4);
        executor.submit(futureTask);

        //這裏能夠用一個sleep代替對其餘業務邏輯的處理
        Thread.sleep(0);

        // 獲取真實數據
        try {
            System.out.println("數據=" + futureTask.get());
        }finally {
            executor.shutdown();
        }
    }
}
相關文章
相關標籤/搜索