FutureTask 是什麼?

本文內容如有錯誤、不足之處,歡迎技術愛好者們一同探討,在本文下面討論區留言,感謝。

簡介

Java 中爲了編程異步事件,我們使用 Thread 類和 Runnable 接口,它們可以開發並行應用程序。問題是在執行結束時不能返回值。因此,添加了 FutureTaksFutureCallable 類,它們與以前的類具有大致相同的功能,但極大地促進了並行應用程序的開發。由於線程 Thread 只支持 Runnable 構造,於是有了 Future 可以根據 Callable 構建線程。由於 Future 只是一個接口,無法直接創建對象,因此有了 FutureTask 。 請參閱:

  • Future:封裝並行調用的類,可以取消任務的執行,確定執行是否已成功完成或出錯,以及其他操作;
  • FutureTask:這是 Future 接口的實現,將在並行調用中執行。
  • Callable:用於實現並行執行的接口。它與 Runnable 接口非常相似,但是它不返回任何值,而 Callable 必須在執行結束時返回一個值。
  • ExecutorService:用於在創建線程池,開始和取消管理並行執行的線程。

創建線程的3中方式 ThreadRunnableCallable ,其中 Callable 是有返回結果的,這種特殊性在某種情況下非常有幫助,例如:需要知道每個線程計算金額的多少,那麼需要返回結果並進行保存。

使用

概念

  1. FutureTask 實現 Future 接口和 RunnableFuture 接口,意味着可以將 FutureTask 用作 Runnable ,並且可以將其提交給 ExecutorService 進行執行。
  2. 當調用一個 Future.submit() 來提交一個可調用或可運行的對象時,大多數時候 ExecutorService 將創建 FutureTask ,也可以手動創建它。
  3. FutureTask 就像一個latch鎖。
  4. FutureTask 表示的計算是通過 Callable 接口實現的。
  5. 它實現了 FutureCallable 接口。
  6. get() 方法的行爲取決於任務的狀態。如果任務未完成,則 get() 方法將等待或阻塞,直到任務完成。任務完成後,它將返回結果或引發 ExecutionException

構造方法只有兩個:

// Creates a FutureTask that will, upon running, execute the given Callable.
// 創建一個FutureTask將在運行時執行給定的Callable。
FutureTask(Callable<V> callable)

// Creates a FutureTask that will, upon running, execute the given Runnable, and arrange that get will return the given result on successful completion.
// 創建一個FutureTask將在運行時執行給定的Runnable,並在獲得成功完成後返回給定結果result。
FutureTask(Runnable runnable, V result)
package test;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Java 項目如何使用FutureTask例子
 */
public class FutureDemo {

    private static final ExecutorService threadpool = Executors.newFixedThreadPool(3);

    public static void main(String args[]) throws InterruptedException, ExecutionException {

        FactorialCalculator task = new FactorialCalculator(10);
        System.out.println("Submitting Task ...");

        Future future = threadpool.submit(task);

        System.out.println("Task is submitted");

        while (!future.isDone()) {
            System.out.println("Task is not completed yet....");
            Thread.sleep(1); //睡眠1秒
        }

        System.out.println("Task is completed, let's check result");
        long factorial = future.get();
        System.out.println("Factorial of 1000000 is : " + factorial);

        threadpool.shutdown();
    }

    private static class FactorialCalculator implements Callable {

        private final int number;

        public FactorialCalculator(int number) {
            this.number = number;
        }

        @Override
        public Long call() {
            long output = 0;
            try {
                output =  factorial(number);
            } catch (InterruptedException ex) {
                Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
            }

            return output;
        }

        private long factorial(int number) throws InterruptedException {
            if (number < 0) {
                throw new IllegalArgumentException("Number must be greater than zero");
            }
            long result = 1;
            while (number > 0) {
                Thread.sleep(1); // 等待1秒時間模擬計算
                result = result * number;
                number--;
            }
            return result;
        }
    }

}

輸出結果:

Submitting Task ...
Task is submitted
Task is not completed yet....
Task is not completed yet....
Task is not completed yet....
Task is completed, let's check result
Factorial of 1000000 is : 3628800

原理

原理是將 Runnable 轉換爲 Callable

public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result);
}

採用適配器模式,調用 RunnableAdapter(task, result) 方法來適配

static final class RunnableAdapter<T> implements Callable<T> {
        final Runnable task;
        final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            task.run();
            return result;
        }
}

通過 Callable 的 call() 調用 Runnable 的 run(),把傳入的 T result 作爲 Callable 的返回結果;

總結

通過使用 FutureTask 類以及 Callable 和 Futere 接口,異步 Java 任務的編程變得更加容易,避免了實現此類應用程序以前所需的“麻煩”。

參考資料

How to use Future and FutureTask in Java Concurrency with Example(如何通過示例在Java併發中使用Future和FutureTask

Java FutureTask Example Program (Java FutureTask示例程序

Class FutureTask(FutureTask類

what is the advantage of using FutureTask over Callable?(與Callable相比,使用FutureTask有什麼優勢?

Processamento assíncrono em Java com Future e FutureTask(帶有Future和FutureTask的異步Java處理

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