Java多線程——Callable、Future和FutureTask

通過Thread或Runnable創建的線程,都需要重寫run方法,而run方法的返回是void的,所以使用這種方式無法獲取線程執行結果。但java提供了其他類和方法來獲取線程執行結果,主要的類有Callable、Future和FutureTask。

Callable

Callable是個泛型接口 Callable ,該接口中只有個call()方法,並且返回值也爲 V,常和ExecutorService中的 submit 方法配合使用。

<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);

Future

Future可以對具體的 Runnable 或者 Callable 任務的執行結果進行取消、查詢是否完成、獲取結果等操作。可以通過get方法獲取執行結果,該方法會阻塞直到任務返回結果。

Future 接口中有5個方法:

cancel()方法用來取消任務,如果取消任務成功則返回true,如果取消任務失敗則返回false;

isCancelled方法表示任務是否被取消成功,如果在任務正常完成前被取消成功,則返回 true;

isDone方法表示任務是否已經完成,若任務完成,則返回true;

get()方法用來獲取執行結果,這個方法會產生阻塞,會一直等到任務執行完畢才返回;

get(long timeout, TimeUnit unit)用來獲取執行結果,如果在指定時間內,還沒獲取到結果,就直接返回null。

FutureTask

FutureTask類實現了 RunnableFuture 接口,RunnableFuture 繼承了 Runnable 接口和 Future 接口,所以FutureTask既可以作爲Runnable被線程執行,又可以作爲Future得到Callable的返回值。

FutureTask提供了2個構造器:

public FutureTask(Callable<V> callable) {
}

所以實現了Callable的對象可以通過該構造器得到一個FutureTask,而FutureTask可以直接給線程來執行,然後通過FutureTask取回執行結果。

public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}

可以看出該構造器通過 Executors.callable() 把Runnable轉換爲Callable,其內部使用了適配器 RunnableAdapter。因此FutureTask實現了Future、Runnable,又是包裝了Callable( 如果是Runnable最終也會被轉換爲Callable ), 它是這兩者的合體。

示例代碼

ExecutorService的兩個方法:

<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);

可以看出,通過第一個方法直接執行Callable,然後從Future中獲取結果;或者用 FutureTask(Callable callable) 構造個FutureTask(它實現了Runable接口)對象用第二個方法獲取返回結果,或者直接就在一個線程中執行。

代碼實現:

public class ThreadCallablePractice {

    public void callableTest() throws Exception{

        ExecutorService executorService = Executors.newFixedThreadPool(3);
        Future<String> future = executorService.submit(new Task());
        executorService.shutdown();
        System.out.println(future.get());
    }

    public void futureTaskTest() throws Exception{
        FutureTask<String> futureTask = new FutureTask<String>(new Task());
        //new Thread(futureTask).start();
        //或者仍用線程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        executorService.submit(futureTask);
        executorService.shutdown();
        System.out.println(futureTask.get());
    }

    public static void main(String[] args) throws Exception{
        ThreadCallablePractice practice = new ThreadCallablePractice();
        //practice.callableTest();
        practice.futureTaskTest();

    }
}

class Task implements Callable<String>{

    public String call(){
        System.out.println(Thread.currentThread().getName() + " is working");
        return "callable result";
    }
}

運行結果

pool-1-thread-1 is working
callable result

參考資料

Java併發編程:Callable、Future和FutureTask
Callable和Future、FutureTask的使用

發佈了218 篇原創文章 · 獲贊 267 · 訪問量 67萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章