Guava future

FrankYou發表於2018-11-01

減少主函式的等待時間,使得多工能夠非同步非阻塞執行

ListenableFuture是可以監聽的Future,它是對java原生Future的擴充套件增強。Future表示一個非同步計算任務,當任務完成時可以得到計算結果。如果希望計算完成時馬上就拿到結果展示給使用者或者做另外的計算,就必須使用另一個執行緒不斷的查詢計算狀態。這樣做會使得程式碼複雜,且效率低下。如果使用ListenableFuture,Guava會幫助檢測Future是否完成了,如果完成就自動呼叫回撥函式,這樣可以減少併發程式的複雜度。

1、MoreExecutors

     該類是final型別的工具類,提供了很多靜態方法。例如listeningDecorator方法初始化ListeningExecutorService方法,使用此例項submit方法即可初始化ListenableFuture物件。

2、ListeningExecutorService

     該類是對ExecutorService的擴充套件,重寫ExecutorService類中的submit方法,返回ListenableFuture物件。

3、ListenableFuture

     該介面擴充套件了Future介面,增加了addListener方法,該方法在給定的excutor上註冊一個監聽器,當計算完成時會馬上呼叫該監聽器。不能夠確保監聽器執行的順序,但可以在計算完成時確保馬上被呼叫。

4、FutureCallback

     該介面提供了OnSuccess和OnFailuren方法。獲取非同步計算的結果並回撥。

5、Futures

     該類提供和很多實用的靜態方法以供使用。

6、ListenableFutureTask

     該類擴充套件了FutureTask類並實現ListenableFuture介面,增加了addListener方法。

7、例項程式碼

 

package listenablefuture;

import com.google.common.util.concurrent.*;

import java.util.concurrent.*;

/**
 * @author xfyou
 * @date 2018/11/2
 */
public class ListenableFutureTest {

    private static final ThreadFactory THREAD_FACTORY = new ThreadFactoryBuilder().setDaemon(true).setNameFormat("test-pool-%d").build();
    private static final ExecutorService EXECUTOR_SERVICE = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(false), THREAD_FACTORY);
    private static final ListeningExecutorService SERVICE = MoreExecutors.listeningDecorator(EXECUTOR_SERVICE);

    public static void main(String[] args) {
        // 可以無阻塞的實現多個任務的非同步回撥
        for (int i = 0; i < 10; i++) {
            ListenableFuture<Boolean> booleanTask = SERVICE.submit(new Callable<Boolean>() {
                @Override
                public Boolean call() {
                    try {
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return true;
                }
            });

            try {
                // 使用get()方法會導致當前主執行緒一直阻塞直到非同步執行緒的任務執行完成
                System.out.println(booleanTask.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }

            Futures.addCallback(booleanTask, new FutureCallback<Boolean>() {
                @Override
                public void onSuccess(Boolean result) {
                    System.out.println("BooleanTask: " + result);
                }

                @Override
                public void onFailure(Throwable t) {
                }
            });

            ListenableFuture<String> stringTask = SERVICE.submit(new Callable<String>() {
                @Override
                public String call() {
                    return "Hello World";
                }
            });
            Futures.addCallback(stringTask, new FutureCallback<String>() {
                @Override
                public void onSuccess(String result) {
                    System.out.println(result);
                }

                @Override
                public void onFailure(Throwable t) {
                }
            });
        }
    }

}

 

Java future 和 Guava future的對比

Future 具有侷限性。在實際應用中,當需要下載大量圖片或視訊時,可以使用多執行緒去下載,提交任務下載後,可以從多個Future中獲取下載結果,由於Future獲取任務結果是阻塞的,所以將會依次呼叫Future.get()方法,這樣的效率會很低。很可能第一個下載速度很慢,則會拖累整個下載速度。

Future主要功能在於獲取任務執行結果和對非同步任務的控制。但如果要獲取批量任務的執行結果,從上面的例子我們已經可以看到,單使用 Future 是很不方便的。其主要原因在於:一方面是沒有好的方法去判斷第一個完成的任務;另一方面是 Future的get方法 是阻塞的,使用不當會造成執行緒的浪費。第一個問題可以用 CompletionService 解決,CompletionService 提供了一個 take() 阻塞方法,用以依次獲取所有已完成的任務。第二個問題可以用 Google Guava 庫所提供的 ListeningExecutorService 和 ListenableFuture 來解決。除了獲取批量任務執行結果時不便,Future另外一個不能做的事便是防止任務的重複提交。要做到這件事就需要 Future 最常見的一個實現類 FutureTask 了。Future只實現了非同步,而沒有實現回撥,主執行緒get時會阻塞,可以輪詢以便獲取非同步呼叫是否完成。

在實際的使用中建議使用Guava ListenableFuture來實現非同步非阻塞,目的就是多工非同步執行,通過回撥的方方式來獲取執行結果而不需輪詢任務狀態。

 

相關文章