LoginSignup
21
21

More than 5 years have passed since last update.

Threadのinterruptメソッドの使い方

Posted at

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

基本:Thread.interrupt()

JavaでThreadを止めるためにはinterruptメソッドを使います.

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

th.start();
Thread.sleep(500);
th.interrupt();
実行結果
Test1: Interrupted

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

先にinterruptが呼ばれた場合

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

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()を実行しているわけではありません.
この実行結果は

実行結果
Test2: Interrupted

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

isInterruptedの使用

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

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()で現在のスレッドを取得します.

実行結果
Test3: true
Test3: Interrupted

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

Thread.interrupted()メソッドの使用

さて,ここでThread.interrupted()メソッドの登場となります.

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()メソッドを使用する必要はありません.
これを実行すると

実行結果
Test4: true

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

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

おまけ

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系は別です.ネットワーク系のタイムアウト実装はまたそのうち解説します.

21
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
21