最近、Javaのスレッドの話ばっかり書いてます。
ここまで来たらスレッド極めたいところ。
今日もスレッドの話。
これまで紹介してきた方法では、スレッドは実行しっぱなし。
結果を受け取れてませんでした。
今日は、スレッドで実行した結果を受け取れる「Callable」「Future」を紹介します。
リターンを返すには、Callableを実装しよう
これまでは、Threadを継承したり、Runnableを実装したクラスを呼び出していましたが、リターンを返すには、Callableを実装したクラスを作りましょう。
こんな感じです。
public class クラス名 implements Callable<String等のreturnする型> { // callの中に実際の処理を書く。 public String call(){ // returnが返せる。 return "スレッド側の処理は終了しました。"; } }
ポイントは、「implements Callable」の後にある「<>」の部分ですね。
この記載をジェネリクスといいます。「<>」の中に型を指定できます。
Callableが色んな型でreturnが出来るので、「<>」の中にreturnしたい型を入れます。
上記の例では、「スレッド側の処理は終了しました」という文字列を返すので、「<String>」とするべきですね。
整数を返す場合は、「<Int>」として下さいね。
呼び出しは、ExecutorServiceとFutureインターフェースを使おう
さて、Callableを実装したクラスを呼び出しには、ExecutorServiceとFutureインターフェースを使いましょう。
ExecutorServiceでsubmitメソッドを使えば、カンタンにスレッドを開始できます。
結果は、Futureインターフェースの型で返ってきます。
Futureインターフェースは、スレッドが終わった後のreturnを入れる入れ物です。
未来に値が入ってくるから、Futureなんですかね?(よくわからないけど・・・)
実際のプログラムで書くとこんな感じです。
// ExecutorServiceを生成 ExecutorService ex = Executors.newSingleThreadExecutor(); // Executorにスレッド ThreadRun5() の実行を依頼 Future<ジェネリクスの型> futureResult = ex.submit(new スレッドのクラス名());
Futureインターフェースに値が返ってきたら、以下のようにgetメソッドで取り出して使います。
// スレッドから戻ってきた値を受け取る。 try { // Future型は「getメソッド」で取り出す必要あり。 String result = futureResult.get(); }catch (Exception e){ // ここは例外処理です System.out.println(e); }
サンプルコードで実際に動かしてみる
さて、実際にサンプルコードを動かしてみましょう。
まずは、呼び出されるCallableを実装したクラスから。
1秒おきに「Thread側: *回目の実行です」と出力し、最後に「Thread側の処理は終了しました」とリターンします。
ThreadRun5.java
import java.util.concurrent.Callable; /** * 呼び出される Callableを実装したクラス * 最後に「スレッド側の処理は終了しました」を * returnできることがポイント。 */ public class ThreadRun5 implements Callable<String> { // callの中に実際の処理を書く。 // 今回は、1秒おきに、「Thread側: * 回目の実行です」 // を出力する public String call(){ for(int i=0; i<3; i++){ System.out.println("Thread側: " + i + "回目の実行です"); try{ Thread.sleep(1000); }catch(InterruptedException e){ System.out.println(e); } } // 最後にreturnが返せる。 return "スレッド側の処理は終了しました。"; } }
ThreadCall5.java
次は、呼び出し側のプログラムです。
ExecutorServiceでThreadRun5の起動、Futureインターフェースで値の受け取りをしています。
こちらも1秒おきに「メイン側: *回目の実行です」と表示して、最後に「メイン側の処理は終了しました」と表示します。
合間に、ThreadRun5から返ってきたメッセージ「Thread側の処理は終了しました」も表示します。
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class ThreadCall5 { public static void main(String[] args){ // ExecutorServiceを生成 ExecutorService ex = Executors.newSingleThreadExecutor(); // Executorにスレッド ThreadRun5() の実行を依頼 Future<String> futureResult = ex.submit(new ThreadRun5()); // スレッドとの並行処理がわかりやすいように、 // メイン側でもfor文で「メイン側: *回目の実行です」 // を出力 for(int i=0; i<3; i++){ System.out.println("メイン側: " + i + "回目の実行です"); try{ Thread.sleep(1000); }catch(InterruptedException e){ System.out.println(e); } } // スレッドから戻ってきた値を受け取る。 try { // Future型は「getメソッド」で取り出す必要あり。 String result = futureResult.get(); System.out.println(result); }catch (Exception e){ System.out.println(e); } System.out.println("メイン側の処理は終了しました。"); // 最後にexit(0)で正常終了。 System.exit(0); } }
プログラムの実行結果
プログラムを動かしてみると、こんな感じになります。
平行でメイン側とThread側のメッセージが表示されつつ、「スレッド側の処理は終了しました」というCallableが返したメッセージが表示されているのがわかると思います。
CallableにFutureインターフェース、使いこなせると、一歩上達できそうですね!
以上、ご参考でした。