28-Callable、Future、FutureTask

6541 ワード

Callable、Future、FutureTask
前述の記事では,スレッドを作成する2つの方法について述べたが,1つはThreadを直接継承することであり,もう1つはRunnableインタフェースを実現することである.この2つの方法には、タスクを実行した後に実行結果を取得できないという欠点があります.実行結果を取得する必要がある場合は、変数を共有したり、スレッド通信を使用したりして効果を達成する必要があり、使用するのは面倒です.
Java 1.5からCallableとFutureが提供され、タスクの実行が完了した後にタスクの実行結果を得ることができます.今日はCallable、Future、FutureTaskの3つのクラスの使い方について議論します.
CallableとRunnable
まずjava.lang.Runnableについてお話ししましょう.これはインタフェースで、run()メソッドは1つだけ宣言されています.
public interface Runnable {
    public abstract void run();
}

run()メソッドの戻り値はvoidタイプであるため、タスクの実行後に結果を返すことはできません.
Callableはjava.util.concurrentパッケージの下にあり、インタフェースでもあり、その中には1つのメソッドしか宣言されていませんが、このメソッドはcall()と呼ばれています.
public interface Callable {
    /**
     * 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;
}

これは汎用インタフェースであり,call()関数が返すタイプは伝達されるVタイプであることがわかる.
では、どうやってCallableを使うのでしょうか.一般的には、ExecutorServiceと組み合わせて使用されます.ExecutorServiceインタフェースでは、いくつかのsubmitメソッドのリロードバージョンが宣言されています.
 Future submit(Callable task);
 Future submit(Runnable task, T result);
Future> submit(Runnable task);

最初のsubmitメソッドのパラメータタイプはCallableです.
Callableは一般的にExecutorServiceと連携して使用されていることをしばらく知る必要がありますが、具体的な使い方は後述します.
一般的に,第1のsubmit法と第3のsubmit法を用い,第2のsubmit法はめったに用いられない.
Future
Futureとは、特定のRunnableまたはCallableタスクの実行結果をキャンセルし、完了したかどうかをクエリーし、結果を取得することです.必要に応じてgetメソッドで実行結果を取得できます.このメソッドは、タスクが結果を返すまでブロックされます.
Futureクラスはjava.util.concurrentパッケージの下にあり、インタフェースです.
public interface Future {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

Futureインタフェースに5つのメソッドが宣言されています.次に、各メソッドの役割を順に説明します.
  • cancelメソッドは、タスクをキャンセルするために使用され、タスクのキャンセルに成功した場合はtrueを返し、タスクのキャンセルに失敗した場合はfalseを返します.パラメータmayInterruptIfRunningは、実行中のタスクをキャンセルしても完了していないタスクを許可するかどうかを示し、trueを設定すると、実行中のタスクをキャンセルできることを示します.タスクが完了した場合、mayInterruptIfRunningがtrueであろうとfalseであろうと、このメソッドはfalseを返します.すなわち、完了したタスクをキャンセルするとfalseを返します.タスクが実行中の場合、mayInterruptIfRunningがtrueに設定されている場合はtrueを返し、mayInterruptIfRunningがfalseに設定されている場合はfalseを返します.タスクがまだ実行されていない場合は、mayInterruptIfRunningがtrueであれfalseであれ、trueが返されます.
  • isCancelledメソッドは、タスクがキャンセルに成功したかどうかを示し、タスクが正常に完了する前にキャンセルに成功した場合はtrueを返します.
  • isDoneメソッドは、タスクが完了したかどうかを示し、タスクが完了した場合、trueを返します.
  • get()メソッドは実行結果を取得するために使用され、このメソッドはブロックを生成し、タスクの実行が完了するまで戻ってくる.
  • get(long timeout,TimeUnit unit)は、実行結果を取得するために使用され、指定された時間内に結果が取得されていない場合はnullに直接戻ります.

  • つまりFutureは3つの機能を提供しています.
    1)任務が完成したかどうかを判断する;
    2)任務を中断できる;
    3)タスク実行結果を得ることができる.
    Futureは1つのインタフェースなので、直接オブジェクトを作成するために使用することはできません.そのため、次のFutureTaskがあります.
    FutureTask
    まずFutureTaskの実現を見てみましょう.
    public class FutureTask implements RunnableFuture
    

    FutureTaskクラスではRunnableFutureインタフェースが実装されています.RunnableFutureインタフェースの実装を見てみましょう.
    public interface RunnableFuture extends Runnable, Future {
        void run();
    }
    

    RunnableFutureはRunnableインタフェースとFutureインタフェースを継承し,FutureTaskはRunnableFutureインタフェースを実現していることがわかる.したがって,Runnableとしてスレッドによって実行され,FutureとしてCallableの戻り値を得ることができる.
    FutureTaskには2つのコンストラクタがあります.
    public FutureTask(Callable callable) {
    }
    public FutureTask(Runnable runnable, V result) {
    }
    

    実際、FutureTaskはFutureインタフェースの唯一の実装クラスです.
    使用例
    1.Callable+Futureを使用して実行結果を取得します.
    public class Test {
        public static void main(String[] args) {
            ExecutorService executor = Executors.newCachedThreadPool();
            Task task = new Task();
            Future result = executor.submit(task);
            executor.shutdown();
             
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
             
            System.out.println("        ");
             
            try {
                System.out.println("task    "+result.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
             
            System.out.println("        ");
        }
    }
    
    class Task implements Callable{
        @Override
        public Integer call() throws Exception {
            System.out.println("        ");
            Thread.sleep(3000);
            int sum = 0;
            for(int i=0;i<100;i++)
                sum += i;
            return sum;
        }
    }
    

    実行結果:
            
            
    task    4950
            
    

    2.Callable+FutureTaskを使用して実行結果を取得します.
    public class Test {
        public static void main(String[] args) {
            //     
            ExecutorService executor = Executors.newCachedThreadPool();
            Task task = new Task();
            FutureTask futureTask = new FutureTask(task);
            executor.submit(futureTask);
            executor.shutdown();
             
            //     ,                  ,         ExecutorService,      Thread
            /*Task task = new Task();
            FutureTask futureTask = new FutureTask(task);
            Thread thread = new Thread(futureTask);
            thread.start();*/
             
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
             
            System.out.println("        ");
             
            try {
                System.out.println("task    "+futureTask.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
             
            System.out.println("        ");
        }
    }
    
    class Task implements Callable{
        @Override
        public Integer call() throws Exception {
            System.out.println("        ");
            Thread.sleep(3000);
            int sum = 0;
            for(int i=0;i<100;i++)
                sum += i;
            return sum;
        }
    }
    

    消去可能にするためにFutureを使用しても使用可能な結果が提供されない場合は、Future>形式タイプを宣言し、nullを最下位タスクの結果として返すことができます.