Java ExecutorServiceの使い方、その2

2019年5月28日

Java ExecutorServiceの使い方、その1の続きである。

さて、Futureのget()呼び出しは、処理が値を返すまでブロックする、つまりjoinだったわけだが、処理を中断させてjoinするにはどうすれば良いだろうか?

get()は使えない。

Futureにはcancel()というメソッドがあり、これがスレッドに割り込みをかける役割を果たしているのだが、しかし、その後でget()を使うと、例外が発生してしまう。

  public static void main(String[]args) throws Exception {
    ExecutorService s = Executors.newSingleThreadExecutor();
    Future<?>f = s.submit(()-> {  
      while (!Thread.interrupted()) {
        System.out.println("loop top");
        try {
          Thread.sleep(200);
        } catch (InterruptedException ex) {
          break;
        }
      }
    });
    s.shutdown();

    Thread.sleep(1000);
    f.cancel(true);
    f.get(); // ここで例外発生。java.util.concurrent.CancellationException
  }

cancelの後ではgetは使えないようだ。

うまく行く例

どうもExecutorServiceだけでは何ともできないらしい。以下のようにしてみる。

  public static void main(String[]args) throws Exception {
    ExecutorService s = Executors.newSingleThreadExecutor();
    Semaphore sem = new Semaphore(1);
    sem.acquire();    
    Future<?>f = s.submit(()-> {  
      while (!Thread.interrupted()) {
        System.out.println("loop top");
        try {
          Thread.sleep(200);
        } catch (InterruptedException ex) {
          System.out.println("interrupted " + Thread.interrupted());
          try {
            Thread.sleep(1000);
          } catch (Exception e) {}
          break;
        }
      }
      System.out.println("releasing");
      sem.release();
    });
    s.shutdown();

    Thread.sleep(1000);
    f.cancel(true);
    System.out.println("acquiring");
    sem.acquire();
    System.out.println("acquired");    
  }

これはうまく行く。表示は以下だ。

loop top
loop top
loop top
loop top
loop top
loop top
acauiring
interrupted false
releasing
acquired

要するにExecutorService以外の方法で何とかしなければならないらしい。