天天看點

談談Runnable、Future、Callable、FutureTask之間的關系

注:該篇文章,隻是用來梳理他們之間的關系,分析FutureTask的源碼則會另開篇幅。

建立線程的兩種方式

一種是實作Runnable接口,實作其run方式;另外一種是繼承Thread類,重寫其run方法。

這一點可以從Thread類中的注釋看到:

談談Runnable、Future、Callable、FutureTask之間的關系

Runnable

Thread類也實作了Runnable接口,Runnable接口的源碼如下:

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}      

是以,可以看得出,不管我們是實作Runnable接口,還是繼承Thread類,run方式的傳回類型都是void,我們都無法拿到線程運作的傳回結果。

如果非要拿到線程運作的結果怎麼辦?Callable便應運而生了。

Callable

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}      

Callable還是一個函數式接口,其泛型參數類型與傳回值類型一緻。

為了更清晰的說明他們之間的關系,這裡先放上類圖結構

類圖關系

談談Runnable、Future、Callable、FutureTask之間的關系

Future

Callable接口,隻是用于單純地去獲得傳回值,但是什麼時候去擷取,或者檢視線程運作的狀态,就需要借助Future

Future的源碼如下:

public interface Future<V> {

    //取消任務的執行,任務已經完成或者已經被取消時,調用此方法,會傳回false
    boolean cancel(boolean mayInterruptIfRunning);

    //判斷任務是否被取消
    boolean isCancelled();

    //判斷任務是否完成(正常完成、異常以及被取消,都将傳回true)
    boolean isDone();

    //阻塞地擷取任務執行的結果
    V get() throws InterruptedException, ExecutionException;

    //在一定時間内,阻塞地擷取任務執行的結果
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;

}      

可以看得出來,Future提供了取消任務、擷取任務執行的狀态與擷取結果的能力。

RunnableFuture

public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}      

RunnableFuture繼承了Runnable與Future接口,沒什麼好說的。

以上說的,Runnable、Callable、Future以及RunnableFuture都是接口,真正幹活還得靠FutureTask

FutureTask

FutureTask實作了RunnableFuture接口,而RunnableFuture繼承了Runnable與Future接口,是以FutureTask既可以作為一個Runnable被Thread執行,也可以擷取到Future異步計算的結果。

從上面的類圖可以看到,傳入一個Callable類型的參數可以構造FutureTask對象。

下面這個例子,使用構造出來的FutureTask對象,傳入Thread的構造函數中,調用start方法以啟動線程。

package com.qcy.testFutureTask;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @author qcy
 * @create 2020/09/07 09:27:06
 */
public class Main {

    static class Task implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            //模拟任務耗時
            Thread.sleep(2000);
            return 1;
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //FutureTask接收一個Callable實作類
        FutureTask<Integer> futureTask = new FutureTask<>(new Task());

        //FutureTask實作了RunnableFuture,間接實作了Runnable接口,是以可作為參數傳給Thread的構造方法中
        new Thread(futureTask).start();

        //get()方法會阻塞2秒,輸出1
        int result = futureTask.get();
        System.out.println(result);
    }
}      
package com.qcy.testFutureTask;

import java.util.concurrent.*;

/**
 * @author qcy
 * @create 2020/09/07 09:27:06
 */
public class Main {

    static class Task implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            //模拟任務耗時
            Thread.sleep(2000);
            return 1;
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(1);

        //FutureTask接收一個Callable實作類
        FutureTask<Integer> futureTask = new FutureTask<>(new Task());

        //這裡使用一個Runnable的實作類,是以可以使用FutureTask
        pool.submit(futureTask);

        //get()方法會阻塞2秒,輸出1
        int result = futureTask.get();
        System.out.println(result);

        pool.shutdown();
    }
}      
package com.qcy.testFutureTask;

import java.util.concurrent.*;

/**
 * @author qcy
 * @create 2020/09/07 09:27:06
 */
public class Main {

    static class Task implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            //模拟任務耗時
            Thread.sleep(2000);
            return 1;
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(1);

        //這裡使用一個Callable的實作類,是以可以使用FutureTask
        Future<Integer> future = pool.submit(new Task());

        //get()方法會阻塞2秒,輸出1
        int result = future.get();
        System.out.println(result);

        pool.shutdown();
    }
}