Help us understand the problem. What is going on with this article?

Threadのinterruptメソッドの使い方

More than 3 years have passed since last update.

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

shinido
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした