Threadのinterruptメソッドの使い方

Threadにはinterruptという名前が入ったメソッドが3つ用意されています．
- `Thread#interrupt()`
- `Thread#isInterrupted()`
- `Thread.interrupted()`
ぱっと見はどれがどれか分かりにくいので，解説してみます．

# 基本：Thread.interrupt()
JavaでThreadを止めるためにはinterruptメソッドを使います．

```java:基本的な使い方
Thread th = new Thread( ()-> {
    try {
        Thread.sleep(1000);
    } catch(InterruptedException e){
        System.out.println("Test1: Interrupted");
    }
});

th.start();
Thread.sleep(500);
th.interrupt();
```

```zsh:実行結果
Test1: Interrupted
```

当然ですが，スレッドが始まってだいたい500msecぐらい後にメッセージが出力されます．

# 先にinterruptが呼ばれた場合
とはいえ，スレッドがいつ実行されるかは実行環境次第なので，もしかしたらスレッドが実行されるより前に`th.interrupt()`が実行されるかもしれません．
明示的にそういう状況を作ってみます．

```java:Thread.sleep()より前にinterrupt()する
AtomicBoolean flag = new AtomicBoolean(true);

Thread th = new Thread( ()-> {
    while(flag.get()){
        // Wait
    }

    try {
        Thread.sleep(1000);
    } catch(InterruptedException e){
        System.out.println("Test2: Interrupted");
    }
});
th.start();
th.interrupt();
Thread.sleep(500);

flag.set(false);
```

スレッドはflagがfalseになるまで待機します．なので，`th.interrupt()`が呼ばれたときにスレッドはwhileループの中で，`Thread.sleep()`を実行しているわけではありません．
この実行結果は

```zsh:実行結果
Test2: Interrupted
```

ということで，`Thread.sleep()`が呼ばれた直後にinterruptされます．
この挙動は，例えば**「`Thread.sleep()`の実行中にinterruptされた場合と，それより以前にinterruptされた場合に処理を分けたい」**といった場合に問題となります．

# isInterruptedの使用
ThreadクラスにはisInterrupted()というメソッドが用意されているので使ってみることにします．

```java:isInterruptedの使用
AtomicBoolean flag = new AtomicBoolean(true);

Thread th = new Thread( ()-> {
    while(flag.get()){
        // Wait
    }

    System.out.println("Test3: "+Thread.currentThread().isInterrupted());
    try {
        Thread.sleep(1000);
    } catch(InterruptedException e){
        System.out.println("Test3: Interrupted");
    }
});
th.start();
th.interrupt();
Thread.sleep(500);

flag.set(false);
```

isInterrupted()メソッドはThreadインスタンスに対して実行する必要があるため，Thread.currentThread()で現在のスレッドを取得します．

```zsh:実行結果
Test3: true
Test3: Interrupted
```

大方の予想通り`true`が表示され，interruptされたことを判別できます．ただ，その後`Thread.sleep()`の呼び出しでInterruptedExceptionがスローされます．
当然と言えば当然で，isInterrupted()メソッドはinterruptされたかどうかを確認するだけなので，その後の処理には関係ありません．
isInterrupted()メソッドがtrueだった場合はスレッドを終了すれば問題は無いのですが，処理を続行したい場合には問題となります．
まぁ，そもそも**一度interruptされたスレッドを実行し続けるような設計がおかしい**のですが，そういうクソ設計に出会うことも想定してみようと思います．

# Thread.interrupted()メソッドの使用
さて，ここで`Thread.interrupted()`メソッドの登場となります．

```java:Thread.interruptedメソッドの使用
AtomicBoolean flag = new AtomicBoolean(true);

Thread th = new Thread( ()-> {
    while(flag.get()){
        // Wait
    }

    System.out.println("Test4: "+Thread.interrupted());
    try {
        Thread.sleep(1000);
    } catch(InterruptedException e){
        System.out.println("Test4: Interrupted");
    }
});
th.start();
th.interrupt();
Thread.sleep(500);

flag.set(false);
```

isInterruptedと違い，interruptedメソッドは現在のスレッドに対して実行されるstaticメソッドなので，`Thread.currentThread()`メソッドを使用する必要はありません．
これを実行すると

```java:実行結果
Test4: true
```

ということで，`Thread.sleep()`メソッドは無事に実行され，1秒待機した後にスレッドは終了します．
`Thread.interrupted()`メソッドは**スレッドがinterruptされたかどうかの真偽値を返し，その後状態をリセットする**メソッドです．（interruptedという名前はどうにかならんかったのか）
そのため，`Thread.interrupted()`を2度実行すると2度目はfalseが返ってきます．
この辺りの挙動を理解しないまま`Thread.interrupted()`メソッドを使うのは危険なので，正直使わない方が良いと思います．

今回は`Thread.sleep()`メソッドに対するinterruptの例を示しましたが，実際には`Thread#join()`や`Semaphore#acquire()`など，様々なブロックする場面で使用します．スレッドを適切に削除するためにもinterruptメソッドは適切に使ってください．

# おまけ

```java:ServerSocket#accept()メソッドの場合
Thread th = new Thread( ()-> {
    try {
        ServerSocket s = new ServerSocket(0);
        s.accept();
    } catch(IOException e){
        System.out.println("Test5: Interrupted");
    }
});
th.start();
Thread.sleep(500);
th.interrupt();
```

この場合，interruptされません．I/O系は別です．ネットワーク系のタイムアウト実装はまたそのうち解説します．