web-dev-qa-db-ja.com

現在実行中のすべてのスレッドを見つけて停止する方法は?

マルチスレッドのJavaプロジェクトがあり、実行中のすべてのスレッドを停止するためにstop()メソッドを追加したい。問題は、このプロジェクトが他の誰かによって開発されていること複数のスレッドを実装する方法に精通しています。

私が知っていることは、プロジェクトが開始されると、多くのスレッドが呼び出され、永久に実行されるということです。実行中のすべてのスレッドを見つけて停止する方法はありますか?私はよく検索して、実行中のスレッドのリストを取得する方法を見つけました。

Set<Thread> threadSet = Thread.getAllStackTraces().keySet();

実行中のすべてのスレッドを停止するには、次に何をしますか?

これらのスレッドを停止する理由は、このプロジェクトをバンドルとしてOSGiコンテナーにデプロイする必要があるためです。バンドルが開始されると、複数のスレッドが永久に実行されます。したがって、すべてのスレッドを停止してバンドルのライフサイクルを制御するには、destroy()メソッドを実装する必要があります。

どう?

for (Thread t : Thread.getAllStackTraces().keySet()) 
{  if (t.getState()==Thread.State.RUNNABLE) 
     t.interrupt(); 
} 

for (Thread t : Thread.getAllStackTraces().keySet()) 
{  if (t.getState()==Thread.State.RUNNABLE) 
     t.stop(); 
}
16
Li'

これは危険な考えです。 Thread.stop() のJavadocの説明:

この方法は本質的に安全ではありません。 Thread.stopを使用してスレッドを停止すると、ロックされているすべてのモニターのロックが解除されます(未確認のThreadDeath例外がスタックを伝播する結果として)。これらのモニターによって以前に保護されたオブジェクトのいずれかが一貫性のない状態であった場合、破損したオブジェクトは他のスレッドから見えるようになり、潜在的に任意の動作を引き起こします。 stopの多くの用途は、ターゲットスレッドの実行を停止する必要があることを示す変数を単に変更するコードに置き換える必要があります。ターゲットスレッドは、この変数を定期的にチェックし、変数が実行を停止することを示している場合は、runメソッドから正常に戻る必要があります。ターゲットスレッドが(たとえば、条件変数で)長時間待機する場合、割り込みメソッドを使用して待機を中断する必要があります。

基本的に、スレッドは安全に終了するように構築および設計する必要があり、任意のスレッドを安全に強制終了することはできません。かなり標準的なパターンは次のように実装されます。

_public abstract class StoppableRunnable implements Runnable {
    private volatile boolean stopWork;;
    private boolean done;

    public final void run() {
        setup();
        while(!stopWork && !done) {
            doUnitOfWork();
        }
        cleanup();
    }

    /**
     * Safely instructs this thread to stop working,
     * letting it finish it's current unit of work,
     * then doing any necessary cleanup and terminating
     * the thread.  Notice that this does not guarentee
     * the thread will stop, as doUnitOfWork() could
     * block if not properly implemented.
     */
    public void stop() {
        stopWork = true;
    }

    protected void done() {
        done = true;
    }

    protected void setup() { }
    protected void cleanup() { }

    /**
     * Does as small a unit of work as can be defined
     * for this thread.  Once there is no more work to
     * be done, done() should be called.
     */
    protected abstract void doUnitOfWork();
}
_

あなたはこれらのスレッドの作成者ではないことを暗示しており、これらのスレッドは安全に停止できない可能性があることを示唆しています。このような場合、 Thread.interrupt() を呼び出して、スレッドに実行内容を停止するよう指示することができます(上記のパターンの代わりに、Thread.interrupt()を使用して同様の効果)しかし、スレッドの設計者が割り込みを処理するためにそれを書いていない場合、これは何もしないか、矛盾した状態や他のエラーを引き起こさないかもしれません。

最終的に、Thread.stop()は、「[スレッドを強制的に実行を停止する]」だけで、スレッドの実装を変更できない場合に必要なものです。ただし、Unixでkillを使用するのと同様に、これは危険な命題です。この方法でスレッドを終了した後は、JVMが不安定で修復不可能な状態にあると考えてください。その後可能。


中断して停止するという提案について:

ここにはまだ多くの問題があります。特に、割り込みはスレッドがすぐに割り込みすることを保証しません(明示的にはそれほどではありませんが上記の私のStoppableRunnableと同様に動作します)、代わりにスレッドが割り込みを行うべきフラグを設定します可能。これは、Thread.interrupt()を呼び出すことができ、スレッドが適切な割り込み処理動作を開始し、その途中でThread.stop()への呼び出しが起動し、スレッドを激しく殺してJVMを破壊する可能性があることを意味します。 Thread.interrupt()の呼び出しは、スレッドがその割り込みにいつまたはどのように応答するかについての保証を提供しません。そのため、StoppableRunnableの明示的な動作を好むのです。言うまでもなく、Thread.stop()を呼び出す場合、最初にThread.interrupt()を呼び出すことで得られることはほとんどありません。推奨しませんが、最初にThread.stop()を呼び出すこともできます。

さらに、ループを実行しているコードはスレッド内でであることを認識してください。つまり、ループは最初に自分自身を終了させ、他のすべてのスレッドを実行したままにすることができます。

21
dimo414

私はこのコードを使用しました:

package com.xxx;

import com.xxx.tools.messageLog;
import Java.util.logging.Level;
import Java.util.logging.Logger;

/**
 *
 * @author Mostafa Nazari
 */
public class ProcessAgent extends Thread{

    public ProcessAgent() {
        super("Process-Agent");
    }


    @Override
    public void run() {
        try {
            com.xxx.Process.agent.Main.main(new String[]{});
        } catch (Exception ex) {
            if (ex instanceof InterruptedException) {
                messageLog.stdwar("Process Agent Stopped");
            }else {
                messageLog.stderr("FATAL Error: "+ex.getMessage());
                Logger.getLogger(ProcessAgent.class.getName()).log(Level.SEVERE, null, ex);            
            }
        }    
    }

    public void terminate() {
        if (super.isAlive()) {
            ThreadGroup group = super.getThreadGroup();
            while (group != null) {
                group .interrupt();
                group = super.getThreadGroup();
            }
            super.interrupt();
        }
    }

    public String getStatus() {
        if (super.isAlive()) {
            return "running";
        } else {
            if (super.isInterrupted()) 
                return "stopping";
            else
                return "stopped";
        }
    }

}

この問題に役立つことを願っています

1
Mostafa Nazari

通常のThreadを作成する代わりに、 ExecutorService または ThreadPoolExecutor のような高度な concurrent APIを使用します

この投稿でさまざまなAPIを見ることができます。

JavaのFork/JoinとExecutorService-どちらを使用するか

ExecutorServiceの強制シャットダウンについては、以下のSEの質問を参照してください。

強制的にシャットダウンする方法Java ExecutorService

0
Ravindra babu