
「こんにちは、アミーゴ!」
「最初に作成された時点では完璧なものはありません。同じことがスレッドにも当てはまります。やがて、Java の作成者は Runnable インターフェイスの欠点を確信するようになりました。Runnable インターフェイスは例外のスローをサポートしておらず、スレッドを見つけることもできませんでした。」タスクの実行結果…」
「Runnable インターフェイスは、一度に数十個を実行して結果を収集するような小さなサブタスクよりも、大規模な独立したタスクに適しています。」
「それが、Callableインターフェイスが発明された理由です。これは、汎用インターフェイスであることもあり、 RunnableやThreadよりも小さなタスクの並列実行にはるかに適しています。」
「インターフェースの典型的な実装は次のとおりです。」
class ReverseString implements Callable<String>
{
String str;
ReverseString(String str)
{
this.str = str;
}
public String call() throws Exception
{
StringBuilder builder = new StringBuilder(str);
builder.reverse();
return builder.toString();
}
}
「Runnableとは異なり、ここでは type 引数で指定された型の結果を返す call メソッドをオーバーライドする必要があります。このアプローチは、 void を返す Runnable インターフェイスの run メソッドよりもはるかに便利です。場合によっては、開発者が次のことを考え出す必要がありました。」スレッドの結果を取得するためのさまざまな回避策。」
"そうか。"
「そして次に、Callable がThreadPoolExecutor とどのように連携できるかを見てみましょう。
「まず、ThreadPoolExecutorクラスの submit メソッドは、パラメーター化された Future オブジェクトを返します。このオブジェクトを使用して、タスクが終了したかどうかを確認し、結果を取得できます。」
「仕組みは次のとおりです。」
// 1. Create a ThreadPoolExecutor
ExecutorService service = Executors.newFixedThreadPool(5);
// 2. Add a task to it
Future<String> task = service.submit(new ReverseString("Amigo"));
// 3. Wait until the task is done
while(!task.isDone()) { Thread.sleep(1); }
// 4. Try to get the result
//We will get either the result, or an exception if one occurred while the task was being executed
try { System.out.println("Full string : " + task.get()); } catch (Exception ie) { ie.printStackTrace(System.err); }
// 5. Stop the ThreadPool.
service.shutdown();
「とんでもない!私は特に Future クラスが好きです。どのようなメソッドがありますか?」
「最も興味深いのは次のとおりです。」
方法 | 説明 |
---|---|
|
タスクを停止します。 |
|
タスクが停止した場合は true を返します。 |
|
タスクの実行が完了した場合は true を返します。 |
|
call メソッドの結果を返すか、例外が発生した場合は例外をスローします。 |
「すごいですね!タスクを停止することもできるんですね。」
「これにあまり依存しないでください。すべてのスレッドを停止できるわけではありません。しかし、タスクがまだキューにある場合は、これで問題なく動作します。」
「私はこのアプローチが気に入っています。自分でスレッドを作成してそこから結果を引き出すよりもはるかに便利です。」
「よかった。今日はこれで終わります。」
GO TO FULL VERSION