Java Callable Futureの例

JavaのCallableとFutureは、マルチスレッドプログラミングでよく使用されます。前のいくつかの投稿では、Javaスレッドについて多くを学びましたが、時にはスレッドが使用できる値を返すことができれば便利です。Java 5では、java.util.concurrent.Callableインターフェースが並行性パッケージで導入されました。このインターフェースはRunnableインターフェースに似ていますが、任意のオブジェクトを返すことができ、例外をスローすることもできます。

Java Callable

Java Callableインターフェースは、ジェネリックを使用してオブジェクトの戻り値の型を定義します。Executorsクラスは、スレッドプールでJava Callableを実行するための便利なメソッドを提供します。Callableタスクは並行して実行されるため、戻り値を待つ必要があります。

Java Future

Java Callableタスクはjava.util.concurrent.Futureオブジェクトを返します。Java Futureオブジェクトを使用すると、Callableタスクのステータスを確認し、返されたオブジェクトを取得できます。それはget()メソッドを提供しており、これによりCallableが終了するのを待ってから結果を返すことができます。Java Futureには関連するCallableタスクをキャンセルするためのcancel()メソッドがあります。結果を待つための時間を指定できるget()メソッドのオーバーロードバージョンもあり、これは現在のスレッドが長時間ブロックされるのを避けるのに役立ちます。関連するCallableタスクの現在のステータスを調べるためのisDone()およびisCancelled()メソッドもあります。ここに、1秒後にタスクを実行するスレッドの名前を返すJava Callableタスクの単純な例があります。我々はExecutorフレームワークを使用して100のタスクを並列に実行し、送信されたタスクの結果を取得するためにJava Futureを使用しています。

package com.journaldev.threads;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
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;

public class MyCallable implements Callable {

    @Override
    public String call() throws Exception {
        Thread.sleep(1000);
        //このcallableタスクを実行するスレッド名を返す
        return Thread.currentThread().getName();
    }
    
    public static void main(String args[]){
        //ExecutorsユーティリティクラスからExecutorServiceを取得し、スレッドプールのサイズは10です
        ExecutorService executor = Executors.newFixedThreadPool(10);
        //Callableに関連付けられたFutureオブジェクトを保持するリストを作成します
        List> list = new ArrayList>();
        //MyCallableインスタンスを作成します
        Callable callable = new MyCallable();
        for(int i=0; i< 100; i++){
            //スレッドプールで実行されるCallableタスクを送信します
            Future future = executor.submit(callable);
            //Futureをリストに追加し、Futureを使用して戻り値を取得できます
            list.add(future);
        }
        for(Future fut : list){
            try {
                //Futureの戻り値を出力し、コンソールでの出力の遅延に注意してください
                //なぜなら、Future.get()はタスクが完了するのを待つからです
                System.out.println(new Date()+ "::"+fut.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
        //今すぐ実行者サービスをシャットダウンします
        executor.shutdown();
    }

}

上記のプログラムを実行すると、出力に遅延があることがわかります。なぜなら、Java Future get()メソッドがJava Callableタスクの完了を待つからです。また、これらのタスクを実行するスレッドは10個しかありません。以下は、上記プログラムの出力の一部のスニペットです。

Mon Dec 31 20:40:15 PST 2012::pool-1-thread-1
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-2
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-3
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-4
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-5
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-6
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-7
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-8
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-9
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-10
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-2
...

ヒント: Java Futureインタフェースの一部のメソッドをオーバーライドしたい場合、例えばget()メソッドを無期限ではなく、あるデフォルトの時間後にタイムアウトするようにオーバーライドしたい場合は、Java FutureTaskクラスが便利です。これはFutureインタフェースの基本実装です。このクラスについて詳しくは、Java FutureTask Exampleをチェックしてください。

Source:
https://www.digitalocean.com/community/tutorials/java-callable-future-example