3

Java 割り込みメカニズムを使用して作業を完了した経験がありますが、現在、現在のスレッドの割り込みステータスをいつ設定する必要があるのか​​ 、いつ InterruptedException をスローする必要があるのか​​ について明確ではありませんか?

そして、より明確にするために、以前にコーディングしたサンプルを次に示します。

これは、作業を開始する前のコードです。

/*
 * Run a command which locates on a certain remote machine, with the
 * specified timeout in milliseconds.
 * 
 * This method will be invoked by means of
 *     java.util.concurrent.FutureTask
 * which will further submmited to a dedicated
 *     java.util.concurrent.ExecutorService
 */
public void runRemoteSript(String command, long timeout) {
    Process cmd = null;
    try {
        cmd = Runtime.getRuntime().exec(command);

        boolean returnCodeOk = false;
        long endTime = System.currentTimeMillis() + timeout;

        // wait for the command to complete
        while (!returnCodeOk && System.currentTimeMillis() < endTime) {

            // do something with the stdout stream
            // do something with the err stream

            try {
                cmd.exitValue();
                returnCodeOk = true;
            } catch (IllegalThreadStateException e) { // still running
                try {
                    Thread.sleep(200);
                } catch (InterruptedException ie) {
                    // The original code just swallow this exception
                }
            }
        }

    } finall {
        if (null != cmd) {
            cmd.destroy();
        }
    }
}

私の意図は、一部のリモート スクリプトが完了するまでに多くの時間を消費するため、コマンドを中断することです。したがって、runRemoteScript は完了したり、手動で停止したりできます。そして、更新されたコードは次のとおりです。

public void cancel(String cmd) {
    // I record the task that I've previously submitted to
    // the ExecutorService.
    FutureTask task = getTaskByCmd(cmd);

    // This would interrupt the:
    //     Thread.sleep(200);
    // statement in the runRemoteScript method.
    task.cancel(true);
}

public void runRemoteSript(String command, long timeout) {
    Process cmd = null;
    try {
        cmd = Runtime.getRuntime().exec(command);

        boolean returnCodeOk = false;
        long endTime = System.currentTimeMillis() + timeout;

        // wait for the command to complete
        **boolean hasInterruption = false;**
        while (!returnCodeOk && System.currentTimeMillis() < endTime) {

            // do something with the stdout stream
            // do something with the err stream

            try {
                cmd.exitValue();
                returnCodeOk = true;
            } catch (IllegalThreadStateException e) { // still running
                try {
                    Thread.sleep(200);
                } catch (InterruptedException ie) {
                    // The updated code comes here:
                    hasInterruption = true; // The reason why I didn't break the while-loop
                                            // directly is: there would be a file lock on
                                            // the remote machine when it is running, which
                                            // will further keep subsequent running the same
                                            // script. Thus the script will still running
                                            // there.
                }
            }
        }

        // let the running thread of this method have the opportunity to
        // test the interrupt status
        if (hasInterruption) {
            Thread.currentThread().interrupt();
        }

        // Will it be better if I throws a InterruptedException here?

    } finall {
        if (null != cmd) {
            cmd.destroy();
        }
    }
}

要するに、スレッドを呼び出してテストするための割り込みステータスを設定した方がよいのでしょうか。それとも、呼び出し元に新しい InterrutedExeptionをスローするだけですか? 上記のアプローチのいずれかがより適しているベストプラクティスまたは特定の状況はありますか?

ここに私の理解を書き留めておきますので、私がそれらのいずれかを誤解した場合は修正してください. 私の理解によると、thread.interrupt() は、クライアント コードが割り込みを処理する必要がないことを意図していると思います。それをテストするかどうかを決定するのはクライアント コードの責任ですが、スローされた InterruptedException は必須です。チェック例外?

4

4 に答える 4

6

この他の回答を参照してください。これは、割り込みに関する非常に優れた議論にリンクしています。InterruptedException主な考え方は、不可能な場合を除いて、 を投げるべきだということです。それが不可能な場合は、 を呼び出して中断状態をリセットする必要がありますThread.currentThread().interrupt()

中断された例外を再スローできないのはなぜですか? Thread.sleep()Runnable を実装するクラスのメソッド内で呼び出している可能性がありますrun()(それはあなたのようには見えません)。InterruptedException がチェックされ、runnable インターフェイスで run メソッドが例外をスローするように宣言されていないため、例外を再スローすることはできません。その場合、中断された状態をリセットするのが最善であり、Runnable を使用する多くのコンテナーはそれを適切に処理します。

いずれにせよ、例外を飲み込まないようにコードを変更することで、正しいことを行っています。

于 2013-11-13T04:54:19.383 に答える
1

スレッドの中断は、可能であれば現在のタスクを放棄して、できるだけ早く実行を終了するようにこのスレッドに指示するメカニズムです。通常、これはスレッドを終了するために呼び出され、コードはそれを適切に尊重する必要があります。これは、グローバルなtry/catch ブロックを使用することで実行できます。これにより、スレッドは while ループがあればすぐにジャンプします。

ブロッキング コードを使用すると、内部コードが最終的に適切なタイミングで InterruptedException をスローすると想定できます。計算が長いだけの場合は、中断を頻繁に確認する必要があります。

ブロッキング コードの正しい実装は次のようになります。

public void run() {
  try {
    while (this.running) {
      doSomethingThatBlocks();
    }
  } catch (InterruptedException e) {
    // maybe log if this wasn't expected
  } finally {
    cleanup();
  }
}

割り込みを自分でチェックする必要があるノンブロッキング コードの場合も、次のようになります。

public void run() {
  while (this.isInterrupted() == false) {
    calculation.nextStep();
  }
}

ブロッキング API 呼び出しを設計している場合にのみ、実際に InterruptedException をスローする必要があります。しかし、潜在的にブロックできるほとんどすべての Java 関数は、必要に応じて既に InterruptedException をスローしているため、自分でスローする必要があるのは通常、特殊なケースです。

既存の API 呼び出しのラッパーのようなものがあり、クリーンアップのために割り込みをインターセプトする必要がある場合は、完了したら同じ割り込みをスローします。割り込みを投げても誰も傷つくことはありません。結局のところ、それは単なる終了の合図です。

于 2013-11-13T13:39:59.410 に答える
0

thread.interrupt() を使用する場合

頻繁にスレッドを中断する必要があるとき。

そしていつ InterruptedException をスローするか

キャッチして再スローする必要がある場合にのみ、これはめったにないはずです。

非常に貧弱な例を選択したことに注意してください。を呼び出すだけProcess.waitFor()で、戻り時に必要な場合は終了値を取得できます。

于 2013-11-13T05:06:51.090 に答える