Java中的Future和FutureTask探索

什麼時候要用到Future?

當程序的執行需要依賴於另一個線程的執行完成或計算結果時,這時候就需要線程阻塞等待另一個線程的執行。Future的get()方法會阻塞當前線程,直到另一個線程執行完畢並返回結果。

什麼是Future?

Future是一個接口,提供了一些方法定義,用於控制任務的執行及獲取執行狀態及結果,源碼如下:

public interface Future<V> {

	/**
    * 取消執行
    * 參數表示是否允許取消正在執行的任務
    */
    boolean cancel(boolean mayInterruptIfRunning);
    
    /**
    * 是否已取消執行
    */
    boolean isCancelled();
    
     /**
    * 是否已完成執行
    */
    boolean isDone();
    
      /**
    * 獲取執行結果(阻塞)
    */
    V get() throws InterruptedException, ExecutionException;

	 /**
    * 特定時間後獲取執行結果,如果到時未執行完畢返回null
    */
    V get(long timeout, TimeUnit unit)  throws InterruptedException, ExecutionException, TimeoutException;
    
}

通過Future獲取工作線程執行結果
public static void main(String[] args) {

        System.out.println("主線程開始執行。。。");
        ExecutorService executorService = Executors.newCachedThreadPool();
        Future<Integer> future = executorService.submit(new Task());
        try {
            Integer integer = future.get();
            System.out.println("獲取工作線程執行結果:" + integer);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("主線程執行完畢。。。");
        
 }

static class Task implements Callable<Integer> {

        @Override
        public Integer call() throws Exception {
            System.out.println("工作線程開始執行。。。");
            int value = 0;
            for (int i = 0; i < 5; i++) {
                value++;
                System.out.println(value);
                Thread.sleep(1000);
            }
            System.out.println("工作線程執行完畢。。。");
            return value;
        }
    }

執行打印結果:

主線程開始執行。。。
工作線程開始執行。。。
1
2
3
4
5
工作線程執行完畢。。。
獲取工作線程執行結果:5
主線程執行完畢。。。

上述示例中
通過線程池submit()方法開啓工作線程任務的執行,實際上這個方法還有一些重載的方法,不僅可以傳人Callable還可以傳入Runnable,只不過Callable可以直接通過泛型確定返回的數據類型,Runnable則稍顯麻煩。從下面重載的方法來看,要麼直接傳入Runnable,要麼調用兩個參數的重載方法,給該工作線程一個固定的返回結果,但是無論哪種方式都沒辦法將run()方法內的程序計算結果獲取到,因爲run()方法沒有返回值。而Callable的call()方法則可以返回程序的計算結果。

public interface Runnable {
    public abstract void run();
}

public interface Callable<V> {
    V call() throws Exception;
}
public abstract class AbstractExecutorService implements ExecutorService {

    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

    public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }

    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }
}
什麼是FutureTask

FutureTask從類名上就可以看出來是對工作任務的封裝,同時聯繫到Future那麼它應該還包含了Future的一些特性。它有兩個構造方法,可以傳入Callable,可以傳入Runnable,Runnable最終也會被封裝成Callable:

public class FutureTask<V> implements RunnableFuture<V> {
 	public FutureTask(Callable<V> callable) {
     	   if (callable == null)
      	      throw new NullPointerException();
    	    this.callable = callable;
    	    this.state = NEW;       // ensure visibility of callable
  	 }
  	 
   	 public FutureTask(Runnable runnable, V result) {
     	   this.callable = Executors.callable(runnable, result);
     	   this.state = NEW;       // ensure visibility of callable
     }
     
	......
	
	public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }
    
    ......
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

FutureTask是RunnableFuture的實現類,看一下RunnableFuture的定義,RunnableFuture接口同時繼承了Runnable和Future,所以FutureTask本質上其實是對Runnable接口的擴展,也是對構造方法傳入的Runnable或Callable對象的進一步封裝,增加了對執行任務的控制及狀態記錄。

既然是作爲Runnable來使用那麼它的任務調用邏輯一定是在自身對run()實現中,從上面源碼中可以看到run()方法內部實際上是通過Callable的call方法實現的,這樣就達到了獲取返回值的目的。

通過FutureTask獲取工作線程執行結果

對於上述示例我們看一下通過FutureTask獲取其執行結果

 public static void main(String[] args) {
 
        System.out.println("主線程開始執行。。。");
        ExecutorService executorService = Executors.newCachedThreadPool();
        FutureTask<Integer> futureTask = new FutureTask<>(new Task());
        executorService.submit(futureTask);
        
        try {
            Integer integer = futureTask.get();
            System.out.println("獲得子線程執行結果:" + integer);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("主線程執行完畢。。。");
        
 }

本質上是把FutureTask當成了Runnable傳遞給ExecutorService接口的submit()方法,前面提到submit()方法有三個不同的重載方法,那麼看一下他們有什麼區別,submit()具體實現是在抽象類AbstractExecutorService中:

public abstract class AbstractExecutorService implements ExecutorService {

	 public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

    public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }

    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

	 protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }

    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }
}

三個重載的submit()的方法從其實現上來看,不管傳入的是Runnable還是Callable最終都是被封裝成FutureTask,最後交由ExecutorService.execute執行。

因此通過Future獲取工作線程的執行結果,本質上還是通過FutureTask.get方法來實現的(從源碼中可以很清晰的看到,get()方法內部會維護一個無限的for循環阻塞當前線程,輪詢Task的執行狀態,直到獲任務完成、線程被打斷或者出現異常後終止循環返回執行結果)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章