6

Java でスレッドがどのように機能するかを理解しようとしており、現在、キャンセル可能なループ スレッドを実装する方法を調査しています。コードは次のとおりです。

public static void main(String[] args) throws Exception {
    Thread t = new Thread() {
        @Override
        public void run() {
            System.out.println("THREAD: started");
            try {
                while(!isInterrupted()) {
                    System.out.printf("THREAD: working...\n");
                    Thread.sleep(100);
                }
            } catch(InterruptedException e) {
                // we're interrupted on Thread.sleep(), ok

                // EDIT
                interrupt();

            } finally {
                // we've either finished normally
                // or got an InterruptedException on call to Thread.sleep()
                // or finished because of isInterrupted() flag

                // clean-up and we're done
                System.out.println("THREAD: done");
            }               
        }
    };

    t.start();
    Thread.sleep(500);
    System.out.println("CALLER: asking to stop");
    t.interrupt();
    t.join();
    System.out.println("CALLER: thread finished");
}

私が作成したスレッドは、遅かれ早かれ中断される予定です。そのため、isInterrupted() フラグをチェックして、続行する必要があるかどうかを判断し、一種の待機操作 ( 、、 )InterruptedExceptionにある場合に処理するために catch も行います。sleepjoinwait

明確にしたいことは次のとおりです。

  1. この種のタスクに割り込みメカニズムを使用しても問題ありませんか? (持っているのに比べてvolatile boolean shouldStop
  2. この解決策は正しいですか?
  3. InterruptedException を飲み込むのは正常ですか? 誰かが私のスレッドに割り込みを要求したコードが何であったか、私はあまり興味がありません。
  4. この問題を解決するためのより短い方法はありますか? (主なポイントは「無限」ループを持っていることです)

編集への 呼び出しをinterrupt()in catch for に追加しましたInterruptedException

4

4 に答える 4

4

いいえと答えています。3:

基本的に問題は次のとおりです。中断された例外にはどのような目的がありますか? ブロッキング (睡眠など) をやめて、早く戻るように指示します。

InterruptedException を処理する方法は 2 つあります。

  • スレッドが中断されたままになるように、再スローします
  • もう一度設定Thread.currentThread.interrupt()して、クリーンアップ作業を行ってください。このようにして、スリープを開始するスレッド内の別のメソッドが再びスローされることを確認できます

単純に を飲み込むことInterruptedExceptionは、最終的に終了するという割り込みの目的に関しては良い考えではありません。ただし、中断するように求められるだけなので、片付けの時間はまだあります。

この場合、これは私自身の「過剰反応」かもしれませんが、通常、そのようなコードははるかに複雑であり、このスレッドのフォローアップ コードの一部がブロッキング メソッドを再度呼び出さないことをどうやって知ることができますか?

編集

そうでなければ、あなたがしていることは問題ないと思います。しかし、自分のコードで実際にそれを行っている人を見たことがなかったので、少し驚きました。

そして、その理由を説明する興味深い記事がここにあります: http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html

于 2011-08-11T11:44:32.383 に答える
2
  1. はい、大丈夫です。Thread/Runnable を停止する方法を文書化する必要があります。Runnable 実装に、停止メカニズムをカプセル化する専用の停止メソッドを追加できます。割り込みを使用するか、専用のブール値を使用するか、またはその両方を使用してください。
  2. はい。ただし、InterruptedException をキャッチするときに割り込みステータスを復元することをお勧めします。Thread.currentThread().interrupt();
  3. いいえ、割り込みステータスを復元する必要があります
  4. 私が知っているものはありません
于 2011-08-11T11:47:09.403 に答える
1

1)Java Concurrency in Practiceの本によると、この例の方法は、揮発性フラグ(すでに割り込みフラグがあるため冗長です)を使用するよりも望ましい方法です。これが、InterruptedExceptionsの使用を意図した方法です。

2)はい

3)割り込みフラグの状態を復元する限り、例外を食べることができます。例外はエラーを表すものではないので、それを食べても情報が失われることはありません。これは純粋に制御を移す手段です。(割り込みフラグのステータスを復元することは、スレッドがキャンセルされていることをそれぞれに通知する必要があるネストされた制御構造がある場合に重要です。あなたのような単純な例では、それは良い形式ですが、欠落していても何も害はありません。)

4)いいえ

于 2011-08-11T12:26:41.057 に答える
1

割り込みは使ってもいいですが、うまく使いましょう。Thread.currentThread().interrupt()キャッチを投げ直さなければなりません。理由を示すコードは次のとおりです。

public class MyThread extends Thread {
    private static boolean correct = true;

    @Override
    public void run() {
        while (true) {
            // Do Something 1
            for (int i = 0; i < 10; i++) { // combined loop
                // Do Something 2
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ex) {
                    if (correct)
                        Thread.currentThread().interrupt(); // reinterrupting
                    System.out.println("First Catch");
                    break; // for
                }
            }
            try {
                // Do Something 3
                System.out.print("before sleep, ");
                Thread.sleep(1000);
                System.out.print("After sleep, ");
            } catch (InterruptedException ex) {
                if (correct)
                    Thread.currentThread().interrupt();
                System.out.println("Second catch");
                break; // while
            }
        }
        System.out.println("Thread closing");
    }

    private static void test() throws InterruptedException {
        Thread t = new MyThread();
        t.start();
        Thread.sleep(2500);
        t.interrupt();
        t.join();
        System.out.println("End of Thread");
    }

    public static void main(String[] args)
            throws InterruptedException {
        test();
        correct = false; // test "bad" way
        test();
    }
}

もう1つのことは、Interruptions待っているときに常に機能するとは限りませんInputStreams。その後、 (一部の場合) を使用できますInterruptedIOExceptionが、常に機能するとは限りません。これらのケースを理解するには、次のコードを試してください。

public class Mythread extends Thread {
    private InputStream in;

    public Mythread(InputStream in) {
        this.in = in;
    }

    @Override
    public void interrupt() {
        super.interrupt();
        try {
            in.close(); // Close stream if case interruption didn't work
        } catch (IOException e) {}
    }

    @Override
    public void run() {
        try {
            System.out.println("Before read");
            in.read();
            System.out.println("After read");
        } catch (InterruptedIOException e) { // Interruption correctly handled
            Thread.currentThread().interrupt();
            System.out.println("Interrupted with InterruptedIOException");
        } catch (IOException e) {
            if (!isInterrupted()) { // Exception not coming from Interruption
                e.printStackTrace();
            } else { // Thread interrupted but InterruptedIOException wasn't handled for this stream
                System.out.println("Interrupted");
            }
        }
    }

    public static void test1() // Test with socket
            throws IOException, InterruptedException {
        ServerSocket ss = new ServerSocket(4444);
        Socket socket = new Socket("localhost", 4444);
        Thread t = new Mythread(socket.getInputStream());
        t.start();
        Thread.sleep(1000);
        t.interrupt();
        t.join();
    }

    public static void test2() // Test with PipedOutputStream
            throws IOException, InterruptedException { 
        PipedInputStream in = new PipedInputStream(new PipedOutputStream());
        Thread t = new Mythread(in);
        t.start();
        Thread.sleep(1000);
        t.interrupt();
        t.join();
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        test1();
        test2();
    }
}
于 2011-08-11T12:29:46.057 に答える