【Java】【非同期】ExecutorService / Callable / Future インタフェース


Callableインタフェースを使用した場合

 * ExecutorService.submit()
 => 戻り値に Future が受け取れる。以下のサイトの Future パターンに関連すると思われる
http://blogs.yahoo.co.jp/dk521123/32918692.html

サンプル1

SampleThreadPool.java

import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class SampleThreadPool {

   private static class Task implements Callable<Integer> {
      private Integer sleepTime;

      public Task(Integer sleepTime) {
         this.sleepTime = sleepTime;
      }

      @Override
      public Integer call() throws Exception {
         Thread.sleep(this.sleepTime * 1000);
         System.out.println(
               Thread.currentThread().getId() + " : " + new Date());
         return this.sleepTime;
      }
   }

   public static void main(String[] args) throws InterruptedException {
      ExecutorService executorService = Executors.newFixedThreadPool(2);
      try {
         System.out.println("task start " + new Date());
         Future<Integer> result1 = executorService.submit(new Task(10));
         Future<Integer> result2 = executorService.submit(new Task(3));
         while (true) {
            if (result1.isDone() && result2.isDone()) {
               System.out.printf("All done");
               break;
            } else {
               Thread.sleep(1000);
               System.out.println("not yet."); // ☆☆
            }
         }
      } finally {
         executorService.shutdown();
         executorService.awaitTermination(1, TimeUnit.MINUTES);
      }
      System.out.println("Done : " + new Date());
   }
}

出力結果

task start Wed Jun 25 00:02:21 JST 2014
not yet.
not yet.
11 : Wed Jun 25 00:02:24 JST 2014
not yet.
not yet.
not yet.
not yet.
not yet.
not yet.
not yet.
10 : Wed Jun 25 00:02:31 JST 2014
not yet.
All doneDone : Wed Jun 25 00:02:31 JST 2014

補足

 * ☆☆の行を「System.out.println("not yet. " + result1.get() + " " + result2.get());」
  に置き換えたら出力結果は以下のようになった

task start Wed Jun 25 00:04:48 JST 2014
11 : Wed Jun 25 00:04:51 JST 2014
10 : Wed Jun 25 00:04:58 JST 2014
not yet. 10 3
All doneDone : Wed Jun 25 00:04:58 JST 2014

サンプル2

Main.java

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Main {
   public static void main(String[] args) {

      ExecutorService es = Executors.newSingleThreadExecutor();

      // submit()は、別スレッド上でCallableの処理を開始し、
      // メインスレッドには即座にFutureオブジェクトを返す
      Future<Drawing> future = es.submit(new Callable<Drawing>() {
         // 別スレッドで実行する処理
         @Override
         public Drawing call() throws Exception {
            System.out.println("Callable start");
            SampleFuture.doHeavyJob();
            return new Drawing(1);
         }
      });

      System.out.println("Callable submit.");

      Drawing drawing = null;
      try {
         // Callableの処理が終わり、結果が返ってくるまでブロックされる。
         drawing = future.get();
      } catch (InterruptedException e) {
         e.printStackTrace();
      } catch (Exception e) {
         e.printStackTrace();
      }
      
      String result = drawing.draw();
      System.out.println("Result = " + result);
   }

   private static void doHeavyJob() throws InterruptedException {
      Random random = new Random();
      Thread.sleep(random.nextInt(10000));
   }
}

Drawing.java

public class Drawing {
   private int number;
   public Drawing(int number) {
      this.number = number;
   }
   
   public String draw() {
      return "Hello World - " + this.number;
   }
}

サンプル3

Main.java

import java.util.Calendar;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Main {
   // バインドするサービスクラス群
   private static final Class<?>[] SERVICE_CLASSES = new Class<?>[] {
         SampleServiceA.class, SampleServiceB.class };

   public static void main(String[] args) throws InterruptedException, ExecutionException {
      System.out.println(Calendar.getInstance().getTime().toString() + " START");
      
      CountDownLatch countDownLatch = new CountDownLatch(SERVICE_CLASSES.length);
      ExecutorService executorService = Executors.newCachedThreadPool();
      
      System.out.println(Calendar.getInstance().getTime().toString() + " Create SampleServiceA");
      Future<Object> futureA = executorService.submit(new SampleServiceA(countDownLatch));
      System.out.println(Calendar.getInstance().getTime().toString() + " SampleServiceA.futureA = " + futureA.get().toString());
      
      System.out.println(Calendar.getInstance().getTime().toString() + " Create SampleServiceB");
      Future<Object> futureB = executorService.submit(new SampleServiceB(countDownLatch));
      System.out.println(Calendar.getInstance().getTime().toString() + " SampleServiceB.futureB = " + futureB.get().toString());
      
      System.out.println(Calendar.getInstance().getTime().toString() + " executorService.shutdown() call");
      executorService.shutdown();
      
      System.out.println(Calendar.getInstance().getTime().toString() + " countDownLatch.await() call");
      countDownLatch.await();
      
      System.out.println(Calendar.getInstance().getTime().toString() + " END");
   }
}

SampleServiceA.java

SampleServiceB.java も同じように作る。そこは省略。
import java.util.Calendar;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;

public class SampleServiceA implements Callable<Object> {
   private CountDownLatch countDownLatch;

   SampleServiceA(CountDownLatch countDownLatch) {
      this.countDownLatch = countDownLatch;
   }


   @Override
   public Object call() throws Exception {
      System.out.println(Calendar.getInstance().getTime().toString() + " SampleServiceA.call() START");
      long sleepTime = (long)(5000L * (Math.random() * 9 + 1));
      
      try {
         System.out.println("SampleServiceA.sleepTime = " + sleepTime);
         System.out.println(Calendar.getInstance().getTime().toString() + " SampleServiceA.Sleeping ... ");
         Thread.sleep(sleepTime);
      } catch (Exception ex) {
         ex.printStackTrace();
      } finally {
         this.countDownLatch.countDown();
      }
      System.out.println(Calendar.getInstance().getTime().toString() + " SampleServiceA.call() END");
      return sleepTime;
   }
}

出力結果

Thu Nov 20 16:53:45 JST 2014 START
Thu Nov 20 16:53:45 JST 2014 Create SampleServiceA
Thu Nov 20 16:53:45 JST 2014 SampleServiceA.call() START
SampleServiceA.sleepTime = 7922
Thu Nov 20 16:53:45 JST 2014 SampleServiceA.Sleeping ... 
Thu Nov 20 16:53:53 JST 2014 SampleServiceA.call() END
Thu Nov 20 16:53:45 JST 2014 SampleServiceA.futureA = 7922
Thu Nov 20 16:53:53 JST 2014 Create SampleServiceB
Thu Nov 20 16:53:53 JST 2014 SampleServiceB.call() START
SampleServiceB.sleepTime=79621
Thu Nov 20 16:53:53 JST 2014 SampleServiceB.Sleeping ... 
Thu Nov 20 16:55:13 JST 2014 SampleServiceB.call() END
Thu Nov 20 16:53:53 JST 2014 SampleServiceB.futureB = 79621
Thu Nov 20 16:55:13 JST 2014 executorService.shutdown() call
Thu Nov 20 16:55:13 JST 2014 countDownLatch.await() call
Thu Nov 20 16:55:13 JST 2014 END


関連記事

ExecutorService / Runnableインタフェース

http://blogs.yahoo.co.jp/dk521123/33665815.html

java.util.concurrent

java.util.concurrent について

http://blogs.yahoo.co.jp/dk521123/32538961.html

CountDownLatch ~カウントダウンラッチ~

http://blogs.yahoo.co.jp/dk521123/33538428.html

ExecutorService

http://blogs.yahoo.co.jp/dk521123/33665815.html

ScheduledExecutorService ~スケジューラ~

http://blogs.yahoo.co.jp/dk521123/33934102.html

タイマー

TaskTimer

http://blogs.yahoo.co.jp/dk521123/33926465.html

デザインパターン

Future パターン

http://blogs.yahoo.co.jp/dk521123/32918692.html