Java多執行緒——Callable、Future和FutureTask

gary-liu發表於2017-03-17

通過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的使用

相關文章