技術メモ

技術メモ

ラフなメモ

Javaのinterruptを試す

スレッドのキャンセルについて勉強します。

プログラミング言語 Java 第4版

プログラミング言語 Java 第4版

スレッドへの割り込みに関するメソッドは以下の3つがあります。

  • Thread.interrupt()
  • Thread.interrupted()
  • Thread.isInterrupted()

interruptは、スレッドへの割込みを送ります。isInterruptedはスレッドが割り込まれたかどうかを検査します。interruptedは、staticメソッドであり、カレントスレッドが割り込まれたかどうかを検査し、その後、そのスレッドの「割り込まれた」状態をクリアーします。スレッドの割り込まれた状態は、割り込まれたスレッド自身だけがクリアーできます。


スレッドへの割り込みは、通常はスレッドが行っている事に対して影響したりしませんが、sleepやwait等のいくつかのメソッドは、InterruptedException をスローします。もし、スレッドが割り込まれた時に、スレッドがこれらのメソッドの1つを実行していると、そのメソッドは InterruptedException をスローします。

Thread.sleep() 中での interrupt()

public class Sample {

    public static void main(String[] args) throws InterruptedException {

        Thread task = new Thread(new MySleepTask());
        task.start();

        System.out.printf("[%s] :isInterrupted = %s\n", task.getName(), task.isInterrupted());
        Thread.sleep(3000);

        task.interrupt();

        System.out.printf("[%s] :isInterrupted = %s\n", task.getName(), task.isInterrupted());

    }
}

class MySleepTask implements Runnable {

    @Override
    public void run() {
        try {
            System.out.printf("[%s] :%s\n", Thread.currentThread().getName(), "start!");
            Thread.sleep(10000);
            System.out.printf("[%s] :%s\n", Thread.currentThread().getName(), "finish!");
        } catch (InterruptedException e) {
            System.out.printf("[%s] :%s\n", Thread.currentThread().getName(), "interrupted!");
            e.printStackTrace();
        }
    }
}

結果
interrupt() 発行後に isInterrupted() がtrueになっていることが分かります。 sleep() メソッド実行中に interrupt() されると InterruptedException が発生し、上記のサンプルコードでは、[Thread-0] :finish! となることなく、スレッドが終了していることが分かります。

[Thread-0] :isInterrupted = false
[Thread-0] :start!
[Thread-0] :isInterrupted = true
[Thread-0] :interrupted!
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at sample_14_8.MySleepTask.run(Sample.java:26)
    at java.lang.Thread.run(Thread.java:748)

独自メソッド中での interrupt()

通常は interrupt() されたとしても InterruptedException は発生しないため、以下の独自実装では InterruptedException は発生しません。

package sample_14_8;

public class Sample {

    public static void main(String[] args) throws InterruptedException {

        Thread task = new Thread(new MyTask());
        task.start();

        System.out.printf("[%s] :(%s) isInterrupted = %s\n", task.getName(), Thread.currentThread().getStackTrace()[1].getMethodName(), task.isInterrupted());

        Thread.sleep(3000);
        System.out.printf("[%s] :(%s) %s\n", task.getName(), Thread.currentThread().getStackTrace()[1].getMethodName(), "3000ms slept...");

        task.interrupt();

        Thread.sleep(3000);
        System.out.printf("[%s] :(%s) isInterrupted = %s\n", task.getName(), Thread.currentThread().getStackTrace()[1].getMethodName(), task.isInterrupted());

    }
}

class MyTask implements Runnable {

    @Override
    public void run() {
        System.out.printf("[%s] :(%s) %s\n", Thread.currentThread().getName(), Thread.currentThread().getStackTrace()[1].getMethodName(), "start!");
        doSomething();
        System.out.printf("[%s] :(%s) isInterrupted = %s\n", Thread.currentThread().getName(), Thread.currentThread().getStackTrace()[1].getMethodName(), Thread.currentThread().isInterrupted());
        System.out.printf("[%s] :(%s) %s\n", Thread.currentThread().getName(), Thread.currentThread().getStackTrace()[1].getMethodName(), "finish!");
    }

    public void doSomething() {
        while (!Thread.interrupted()) {
            // 何らかの処理
        }
        System.out.printf("[%s] :(%s) %s\n", Thread.currentThread().getName(), Thread.currentThread().getStackTrace()[1].getMethodName(), "interrupted!");
    }
}

結果
Thread.interrupted() は以下の仕様になっています。

現在のスレッドが割り込まれているかどうかを調べします。このメソッドによりスレッドの割込みステータスがクリアされます。つまり、このメソッドが続けて2回呼び出された場合、2回目の呼出しはfalseを返します(最初の呼出しが割込みステータスをクリアしたあとで、2番目の呼出しがそれを確認する前に現在のスレッドがもう一度割り込まれた場合を除く)。

よって上記の独自クラスの場合は、interrupt() された時にtrueになるが、次に検査するときにはfalseになるので、ビジーループからは抜けているが、出力結果はfalseになっています。

[Thread-0] :(main) isInterrupted = false
[Thread-0] :(run) start!
[Thread-0] :(main) 3000ms slept...
[Thread-0] :(doSomething) interrupted!
[Thread-0] :(run) isInterrupted = false
[Thread-0] :(run) finish!
[Thread-0] :(main) isInterrupted = false