1

これは、割り込みを使用してスレッドを終了/終了しようとする試みと、Ctrl-c 終了の処理に関する 2 番目の投稿です。よくわかりませんが、これが私の最善の試みです。概念をより明確にする必要があります。可能な場合はコード例を示してください。

メイン クラスQuititと別のクラスthethingの 2 つのクラスがあります。メインクラス。

ターミナル経由でプログラムをロードする場合 (Linux の場合):

Java -jar Quitit.jar

Ctrl-c を押して閉じるときは、次のことを行う必要があると言っています。

Runtime.getRuntime().addShutdownHook()

スレッドがシャットダウン時に強制終了されるようにします。

  1. これはそれに対処する正しい方法ですか?
  2. 正常に終了できるようにメソッドを呼び出すことができないのはなぜですか?

Ctrl-C でシャットダウンせず、Thread.Interrupt() でシャットダウンしたい場合、以下のプログラムはそれを正しく使用しますか?

  • Thread.Join() は、対象のスレッドが終了するまで呼び出し元のスレッドを停止してから続行するというのは正しいですか?
  • Thread を拡張する代わりに、 Runnableスレッドの実装で同じRuntime.getRuntime().addShutdownHook()をどのように実装/呼び出しますか?

終了クラス:

public class Quitit extends Thread {

    public Quitit(String name) {
        try {
            connect("sdfsd");
        } catch (Exception ex) {

        }
    }

    void connect(String portName) throws Exception {
        Thread thh = new thething("blaghname");
        Runtime.getRuntime().addShutdownHook(thh);
        thh.start();
        System.out.println("Thread Thh (thething) Started()");

        while (true) {
            try {
                Thread.sleep(2000);

                if (thh.isAlive()) {
                    System.out.println("Thread Thh (thething) isAlive");
                    if (thh.isInterrupted()) {

                        System.out.println("Thread Thh (thething) is Inturrupted Should be Shutting Down");

                    } else {
                        System.out.println("Thread Thh (thething) is Not Inturrupted");
                        thh.interrupt();
                        System.out.println("Thread Thh (thething) Inturrput Sent");
                        System.out.println("Thread Thh (thething) Joined()");
                        thh.join();

                    }

                } else {
                    System.out.println("Thread Thh (thething) isDead");
                    System.out.println("Main Thread:: can now end After Sleep off 2 seconds");
                    Thread.sleep(2000);
                    System.out.println("MMain Thread:: Sleep Ended Calling Break");
                    break;
                }

            } catch (InterruptedException xa) {
                System.out.println("Main Thread:: ending due to InterruptException second Break called");
                break;
            }
        }

        System.out.println("Main Thread:: Outside While(true) via Break call");
    }

    public static void main(String[] args) {
        try {

            Thread oop = new Quitit("");
            Runtime.getRuntime().addShutdownHook(oop);
            oop.start();

        } catch (Exception ezx) {
            System.out.println("Main Thread:: Not Expected Exception");
        }
    }
}

TheThing クラス:

public class thething extends Thread {

    thething(String name) {
        super(name);
    }

    @Override
    public void run() {

        while (true) {
            try {
                System.out.println("thething class:: Inside while(true) Loop, now sleeping for 2 seconds");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                try {

                    System.out.println("thething class:: has been Inturrupted now sleeping for 2 seconds!!");
                    Thread.sleep(2000);
                    break; // Will Quit the While(true) Loop
                } catch (InterruptedException ex) {
                    System.out.println("thething class:: Second InterruptedException called !!");
                }
            }
        }

        System.out.println("thething class:: Outside while(true) and now thread is dying");
    }
}

出力::

run:
Thread Thh (thething) Started()
thething class:: Inside while(true) Loop, now sleeping for 2 seconds
Thread Thh (thething) isAlive
Thread Thh (thething) is Not Inturrupted
Thread Thh (thething) Inturrput Sent
Thread Thh (thething) Joined()
thething class:: has been Inturrupted now sleeping for 2 seconds!!
thething class:: Outside while(true) and now thread is dying
Thread Thh (thething) isDead
Main Thread:: can now end After Sleep off 2 seconds
MMain Thread:: Sleep Ended Calling Break
Main Thread:: Outside While(true) via Break call
FINISHED - BUILD SUCCESSFUL (total time: 8 seconds)
4

4 に答える 4

4

ここまでで、control-c がプログラムを強制終了していないことに驚いています。それは私のテスト プログラムで動作し、ほとんどの Unix バリアントで動作するはずです。

Ctrl-c を押して閉じると、シャットダウン時にスレッドが強制終了されるように (シャットダウン フックをセットアップする) 必要があると言うのは正しいです。

いいえ、これは正しくありません。シャットダウン フックは、一部の処理を明示的にクリーンアップする場合に使用されます。実行中のスレッドやその終了方法とは関係ありません。

正常に終了できるようにメソッドを呼び出すことができないのはなぜですか?

それは仕事じゃないから。

これはそれに対処する正しい方法ですか?

「それ」が何なのかはわかりません。他の人が述べたように、最後の非デーモンスレッドが終了すると、JVM は終了します。その時点で、JVM はすべてのデーモン スレッドを強制終了して終了します。シャットダウン時にバックグラウンド スレッドを強制終了する場合は、次のようにします。

Thething theThing = new TheThing();
// set it to be a daemon thread before it starts
theThing.setDaemon(true);
theThing.start();

スレッドをきれいに終了する適切な方法について質問している場合は、スレッドを使用するvolatile booleanか、スレッドを中断することができます。通常、これは、スレッドを開始するクラスがハンドルを保持することを意味します。QuitItクラスを開始してからTheThing、 を使用している場合は次のようになりますvolatile boolean

void connect(String portName) throws Exception {
    Thread thh = new TheThing("blaghname");
    thh.start();
    ...
    // we are ready to kill the thread now
    tth.shutdown = true;
}

ではTheThingrun()メソッドは次のようになります。

public class TheThing extends Thread {
    volatile boolean shutdown = false;
    public void run() {
       while (!shutdown) {
          ...
          // you can also test for shutdown while processing
          if (shutdown) {
             return;
          }
       }
    }
}

Ctrl-C でシャットダウンせずにシャットダウンしたい場合Thread.interrupt()、以下のプログラムはそれを正しく使用しますか?

を使用Thread.interrupt()することは、スレッドをシャットダウンすることをスレッドに通知する別の方法です。上記のブール値と同様の方法でスレッド割り込みフラグを使用することもできます。

void connect(String portName) throws Exception {
    Thread thh = new TheThing("blaghname");
    thh.start();
    ...
    // we are ready to interrupt the thread now
    tth.interrupt();
}

スレッドを中断すると にフラグが設定されることを理解することは非常に重要Threadです。それらのスレッドは、コードで割り込みを適切に処理する必要があります。ではTheThingrun()メソッドは次のようになります。

public class TheThing extends Thread {
    public void run() {
       while (!Thread.currentThread().interrupted()) {
          ...
       }
    }
}

中断するwait()notify()、、、およびその他のメソッドもスローされInterruptedExceptionます。これに対処する適切な方法は次のとおりです。

try {
   Thread.sleep(1000);
} catch (InterruptedException e) {
   // catching the interrupted exception clears the interrupt flag,
   // so we need to re-enable it
   Thread.currentThread().interrupt();
   // probably you want to stop the thread if it is interrupted
   return;
}

Thread.join() は、対象のスレッドが終了するまで呼び出し元のスレッドを停止してから続行すると言うのは正しいですか?

はい。呼び出しjoin()(引数なし) は、参加しているスレッドが終了するまで、呼び出しスレッドを一時停止します。したがって、通常はシャットダウンフラグを設定するか、スレッドを中断してから参加します。

tth.shutdown = true;
// or tth.interrupt()
tth.join();

Thread を拡張する代わりに、Runnable スレッドの実装で同じ Runtime.getRuntime().addShutdownHook() をどのように実装/呼び出しますか?

この質問は意味がありません。を実装している場合にスレッドをシャットダウンする方法について質問している場合はRunnable、上記と同じメカニズムが機能します。


control-c の議論に影響を与えるもう 1 つの要素は、シグナル ハンドラーです。control-c (およびその他のシグナル) をキャッチできるため、それらを使用してインテリジェントな処理を実行できます。それらは (もちろん) OS に大きく依存しており、割り込みシグナル (通常、SIGINT は control-c によって送信されます) をキャッチして JVM を停止しないと、問題が発生します。

ただし、いずれにせよ、次のようなことができます。

...
MyHandler handler = new MyHandler();
// catch the control-c signal, "TERM" is another common kill signal
Signal.handle(new Signal("INT"), handler);
...

private static class MyHandler implements SignalHandler {
    @Override
    public void handle(Signal arg0) {
        // interrupt your threads
        // clean up stuff
        // set shutdown flags
        // ...
    }
}

繰り返しになりますが、割り込みシグナル (control-c) をキャッチして JVM をダウンさせないのは悪い習慣です。

于 2012-08-01T14:38:17.530 に答える
2

Ctrl-c を押して閉じるときは、次のことを行う必要があると言っているのは正しいですRuntime.getRuntime().addShutdownHook()。スレッドがシャットダウン時に強制終了されるようにする必要があります。

いいえ。

docsに従って、addShutdownHookVM がシャットダウン (-ting) されたときに実行されるスレッドを登録するだけです。理論的には、これはアプリケーション全体のファイナライザーとして使用されます。プロセスが終了するときに実行されるもので、「片付け」ます。ただし、実際には、ファイナライザーが推奨されない理由と同様に、いくつかの問題があります。プロセスが突然終了した場合の実行は保証されていないため、重要なことは何もできません。それらはライフサイクルのデリケートな部分で実行されるため、期待する方法でオブジェクトにアクセスしたり操作したりできない場合があります。そして、それらは迅速に実行する必要があります。そうしないと、終了する前にプロセスが強制終了される危険があります。

とにかく、これはあなたが考えているものとはまったく異なります。VM がシャットダウンすると、必要なくスレッドが終了します。

さらに、開始されていないスレッドをシャットダウン フックとして提供する必要があるため、シャットダウン時にIllegalThreadState例外がスローされないことに少し驚いています。

Ctrl-C でシャットダウンせず、Thread.Interrupt() でシャットダウンしたい場合

これは、マルチスレッド プログラムを終了する「通常の」方法でもありません。

(大まかに) 2 つのオプションがあります。

  1. スレッドを「デーモン」として開始し、そのままにしておきます。非デーモン スレッドが実行されていない場合、プログラムは終了します。そのため、mainスレッドが終了すると、「ワーカー」スレッドがまだ生きていても、プログラムは終了します。もちろん、これはあなたの

  2. 終了する必要があることをスレッドに通知stop()します。たとえば、ブール フラグを設定するメソッドを呼び出して、それらのスレッドに終了を許可させます。run()メソッドが戻ると、スレッドは停止します。通常、スレッドは何らかのループに入っている間だけ実行を続けます。スレッドが終了するように指示されたときに進行中のループを終了させるだけで、通常はフラグをチェックする前に現在の「作業のチャンク」を終了するため、正常にシャットダウンします。

スレッドを中断しても、期待どおりの結果が得られません。内部で別のブール値フラグを設定するだけです。多くのブロック操作 (ファイルシステム/ネットワーク/データベース メソッドなど) は定期的にこのフラグをチェックし、InterruptedException設定されている場合は をスローします。これにより、ブロッキング メソッドを早期に終了できますが、メソッドが「行儀が良い」ものでない場合は保証されません。

したがって、あなたの例ではThread.sleep()、割り込みに応答するため機能し、これを終了するシグナルと見なします。Thread.currentThread().interrupted()ただし、たとえば、100 万番目のフィボナッチ数を計算する独自のメソッドを作成した場合、実装で反復ごとに明示的にチェックしない限り、スレッドを中断しても何も起こりません。

また、割り込みはほとんどどこからでも発生する可能性があり、割り込みに特定の「意味」を帰するのは困難です。それらはスレッドを殺すための合図ですか?クライアントが退屈しているという理由で、現在の方法をあきらめる合図ですか? 新しいデータが到着したので、上から始める合図?重要なプログラムでは、完全に明確ではありません。

これを考えると、ブール値フラグを使用して理由を伝え、フラグを設定した後に中断する方がはるかに優れたアプローチです。中断されたスレッドは、その状態をチェックして何が起こったのかを確認し、何をすべきかを解決する必要があります。たとえば、次のようにコーディングできますthething.run()

private boolean keepRunning = true; 

public void run() {
    while(keepRunning) {
        try {             
            System.out.println("thething class:: Inside while(true) Loop, now sleeping for 2 seconds");
            Thread.sleep(2000); 
        } catch (InterruptedException e) {
            try {
                System.out.println("thething class:: has been Inturrupted now sleeping for 2 seconds!!");                        
                Thread.sleep(2000); 
            } catch (InterruptedException ex) 
            { 
                 System.out.println("thething class:: Second InterruptedException called !!");  
            }
        } 
    }
}

ではQuitIt、中断する前に設定thh.keepRunning = falseします (またはthh.allWorkDone()、フラグを設定するなどのメソッドを定義し、それを呼び出します)

別のスレッドを強制的に停止する方法はないことに注意してください-正当な理由があります-スレッドを停止するように通知し、スレッドで実行されているものはすべてその通知を監視および尊重する必要があります。

于 2012-08-01T09:25:51.120 に答える
2

あなたの質問には多くの質問があります。Ctrl-C が押されたときにスレッドを正常にシャットダウンする場合は、シャットダウン フックを登録し、このシャットダウン フックのコードでスレッドを正常にシャットダウンします。

Thread インスタンスは、Thread を拡張するか、Runnable を Thread コンストラクターに渡すことによって構築できます。したがって、シャットダウン フックのコードを Runnable に実装する場合は、次のようにします。

Runnable r = new Runnable() {
    @Override
    public void run() {
        try {
            // code of the shutdown hook: ask running threads to exit gracefully
            for (Thread t : threadsToShutDown) {
                t.interrupt();
            }
            for (Thread t : threadsToShutDown) {
                t.join(); 
            }
        }
        catch (InterruptedException e) {
            // too bad
        }
    }
};
Runtime.getRuntime().addShutdownHook(new Thread(r));

上記により、スレッドの 1 つが割り込みに応答しない場合、JVM が終了しないことに注意してください。タイムアウトを導入することは良い考えです。

また、シャットダウン フックとして登録したスレッドを開始してはならないことに注意してください。

于 2012-08-01T09:20:56.867 に答える
0

大幅な差で最善-明示的な終了なしでデーモンスレッドを使用します。次に、アプリを再設計して、明示的な終了なしでデーモンスレッドを使用できるようにします。絶対的な最後の手段です。他のアプローチがリモートで不可能な場合は、そのような明示的なシャットダウンコードがここに投稿されています。

于 2012-08-01T10:33:25.913 に答える