Javaでスレッドをどのように殺すのですか?


374

java.lang.ThreadJavaでどうやって殺すの?


2
これまでは、スレッドを強制終了することはできません。デッドロックが発生しやすいためdestroy()が実装されないため
AZ_

1
ExecutorStatusはこの質問に関する答えを好む:stackoverflow.com/questions/2275443/how-to-timeout-a-thread
Kirby

9
@loungerdork「ロックやその他の落とし穴を失うという警告にもかかわらず、Javaは制御できない暴走スレッドに対して安全な停止/破棄メソッドを実装するべきだと思います」したがって、安全でないスレッド停止が必要です。あなたはすでに持っていると思います。
DJクレイワース2015年

4
2009年にどのような質問が212の賛成票を獲得するかは驚くべきことです。これは今日すぐに破棄されます。
Jonathon Reinhart 2016年

7
@JonathonReinhart:なぜですか?最近でも正当な質問のようです。多分あなたは暴走スレッドがあるときにそれが引き起こす欲求不満を知らず、これを何らかの方法で処理するために非推奨の関数しか使用できませんか?
TFuto 2016年

回答:


189

彼らが非推奨になった理由について、SunによるThread.stop()このスレッドを参照してください。これがなぜ悪い方法であったのか、そして一般的にスレッドを安全に停止するために何をすべきかについて詳しく説明します。

彼らが推奨する方法は、バックグラウンドスレッドに停止を要求するフラグとして共有変数を使用することです。この変数は、スレッドの終了を要求する別のオブジェクトによって設定できます。


1
isAlive()を中断したスレッドを確認すると、trueが返され、現在のThreadGroup []に追加され続けます。これは、Thread.currentThread.getThreadGroup()。list();を使用して確認できます。それはそれが持っているすべてのスレッドを出力し、フローを繰り返すと、スレッドの複数のインスタンスが表示されます。
AZ_

3
PCを使用している場合は問題ありませんが、モバイル用のソフトウェアを開発している場合(Androidで体験したことがあります)、OutOfMemoryError
AZ_

2
推奨事項に記載されているように、フラグを介した停止要求の迅速な通信を保証するには、変数が揮発性である(または変数へのアクセスが同期されている)必要があることに注意してください。
mtsz '18 / 06/18

2
このリンクは現時点では削除されています。:でも、私はarchive.orgでそれを見つけることができたweb.archive.org/web/20090202093154/http://java.sun.com/j2se/...
ジェイ・テイラー

2
getConnection()からのメソッドを使用しjava.sql.DriverManagerます。接続の試行に時間がかかりすぎる場合は、呼び出しによって対応するスレッドを強制終了しようとしますThread.interrupt()が、スレッドにはまったく影響しません。Thread.stop()Oracleがあれば、それは動作してはならないと言うが、しかし働きinterrupt()ません。私はそれをどのように機能させ、非推奨のメソッドの使用を避けるのかと思います。
Danny Lo

129

一般的にはしません。

Thread.interrupt()を使用して、実行中の処理を中断するように要求します(javadocリンク)

理由はここのjavadocにあります(java技術情報リンク)。


@Fredrik interrupt()メソッドが呼び出されると、スレッドコンテキストはどうなりますか?主な質問は、新しいスレッドごとのログ生成に関連しています
ABcDexter 2016

3
@ABcDexter重要な点は、割り込みは何も中断せず、スレッド内のコード(またはスレッドによって呼び出されているコード)に対して、誰かが何をしていても中断するように要求したことを通知するだけです。次に、スレッドは処理を適切に停止して戻る必要があります。これは、本来あるべきことを実行した場合と同じです(その時点で、スレッドコンテキストもおそらく破棄されます)。OTOH、スレッドを本当に強制停止した場合、あなたの質問は本当に良くなり、答えは未定義です。
Fredrik、

64

Javaでは、スレッドは強制終了されませんが、スレッドの停止は協調的に行われます。スレッドは終了するように求められ、スレッドは正常にシャットダウンできます。

多くの場合volatile boolean、対応する値に設定されるとスレッドが定期的にチェックして終了するフィールドが使用されます。

私はしません使用してbooleanスレッドがすべきかどうかを確認するために終了しますvolatileフィールド修飾子として使用する場合、これは確実に機能しますが、コードがより複雑になると、代わりにwhileループ内で他のブロッキングメソッドを使用するため、コードがまったく終了しないか、少なくとも、コードが長くなる可能性があります。したいかもしれません。

特定のブロッキングライブラリメソッドは割り込みをサポートしています。

すべてのスレッドにはすでにブールフラグの割り込みステータスがあり、それを利用する必要があります。次のように実装できます。

public void run() {
   try {
      while (!interrupted()) {
         // ...
      }
   } catch (InterruptedException consumed)
      /* Allow thread to exit */
   }
}

public void cancel() { interrupt(); }

Java Concurrency in Practiceから採用されたソースコード。cancel()メソッドはパブリックなので、必要に応じて別のスレッドにこのメソッドを呼び出させることができます。


そして、信頼できないコードをプラグインまたはスクリプトとして実行する場合はどうすればよいでしょうか。Javaには、信頼できないコード用のサンドボックスが組み込まれています。そして、そのサンドボックスは役に立たないので、強制停止せずに作業できます。あなたがJavaでブラウザを書いていると想像してください。任意のページスクリプトを強制終了する機能は貴重です。
ayvango 2015年

@ayvango次に、独自のサンドボックスでそのスクリプトを実行する必要があります。Javaサンドボックスは、アプリケーションの一部ではなく、アプリケーションからマシンを保護します。
David Schwartz 2016

@DavidSchwartzつまり、Java以外のプラットフォームを使用する必要があるということですか?
ayvango 2016

@ayvango Javaサンドボックスを使用して、アプリケーションからマシンを保護できます。ただし、アプリケーションの一部をアプリケーションの他の部分から保護する場合は、それを実行できるツールを選択する必要があります。
David Schwartz

@DavidSchwartz ASFAIKのようなツールは、プラットフォームレベルでサポートされていなければ存在しません。しかしもちろん、このタスクは完全に解釈されたスクリプトエンジンで解決できます。erlangが行うような削減をカウントし、他のことを行うことができます。
ayvango 2016

20

1つの方法は、クラス変数を設定してセンチネルとして使用することです。

Class Outer {
    public static volatile flag = true;

    Outer() {
        new Test().start();
    }
    class Test extends Thread {

        public void run() {
            while (Outer.flag) {
                //do stuff here
            }
        }
    }

}

外部クラス変数を設定します。つまり、上記の例ではflag = trueです。スレッドを「強制終了」するにはfalseに設定します。


2
副次的ヒントとして:スレッドとして実行され、スタックしない場合にのみ、フラグとしての変数が機能します。Thread.interrupt()は、ほとんどの待機状態(待機、スリープ、ネットワーク読み取りなど)からスレッドを解放する必要があります。したがって、これを機能させるために、InterruptedExceptionを決してキャッチしてはなりません。
ReneS 2009年

12
これは信頼できません。volatileどこでも正しく機能するように「フラグ」を作成します。内部クラスは静的ではないため、フラグはインスタンス変数である必要があります。他の操作(割り込みなど)を実行できるように、フラグはアクセサメソッドでクリアする必要があります。「フラグ」という名前は説明的ではありません。
エリクソン2009年

2
runメソッドで "while"を取得しません。これは、runメソッドで記述されたものが繰り返し実行されることを意味しませんか?これは、そもそもスレッドに実行してほしかったものではありません:(

2
(!Thread.currentThread()。isInteruppted())を優先して+1を実行する
Toby

2
どちらの場合も失敗します。たとえば、while内で外部プロセスを開いた場合{// open ext process}、そのプロセスがハングした場合、スレッドが中断されることも、ブール条件をチェックするためにスレッドが最後まで到達することもありません。左にぶら下がっています...たとえば、java.execを使用してpythonコンソールを起動し、exitを書き込まずにコントロールを取得してみてください。そのプロセスを強制終了して抜け出す方法があるかどうかを確認してください...方法はありません。このような状況から抜け出す...
スペースロッカー2012年

11

それを行う方法はあります。しかし、それを使用しなければならなかった場合、あなたは悪いプログラマーであるか、悪いプログラマーによって書かれたコードを使用しています。したがって、悪いプログラマーになるのをやめるか、この悪いコードの使用をやめることを考えるべきです。この解決策は、他に方法がない場合のみです。

Thread f = <A thread to be stopped>
Method m = Thread.class.getDeclaredMethod( "stop0" , new Class[]{Object.class} );
m.setAccessible( true );
m.invoke( f , new ThreadDeath() );

2
Thread.stop非推奨になったとしてもパブリックを呼び出すことができるため、これを行う理由はまったくありません。
Lii

1
@Lii Thread.stopは同じことを行いますが、アクセスと権限もチェックします。使用することThread.stopはかなり明白でありThread.stop0、その代わりに使用した理由を覚えていません。たぶんThread.stop私の特別なケース(Java 6上のWeblogic)ではうまくいかなかったかもしれません。または、Thread.stopが非推奨であり、警告が表示されるためです。
VadimPlatonov、2015年

1
私のシナリオでは、これが、実行中の無限のスレッドを停止する唯一の方法でした。一部の理由では、.stop()はスレッドを停止しませんでしたが、stop0()は停止しました
fiffy

10

蓄積されたコメントに基づいて、いくつかの観察を追加したいと思います。

  1. Thread.stop() セキュリティーマネージャーが許可する場合、スレッドを停止します。
  2. Thread.stop()危険です。そうは言っても、JEE環境で作業していて、呼び出されるコードを制御できない場合は、必要になる可能性があります。Thread.stopが廃止された理由を参照してください
  3. コンテナーワーカースレッドの停止を停止しないでください。ハングしがちなコードを実行する場合は、(慎重に)新しいデーモンスレッドを開始して監視し、必要に応じて強制終了します。
  4. stop()呼び出しスレッドに新しいThreadDeathErrorエラーを作成し、そのエラーをターゲットスレッドにスローします。したがって、スタックトレースは一般的に価値がありません。
  5. JRE 6ではstop()、セキュリティマネージャーに確認し、それを呼び出しstop1()ますstop0()stop0()ネイティブコードです。
  6. Java 13以降Thread.stop()は(まだ)削除されていませんがThread.stop(Throwable)、Java 11で削除されました(メーリングリストJDK-8204243

8

に投票しThread.stop()ます。

たとえば、ネットワークリクエストのように長時間続くオペレーションがあるとします。おそらくあなたは応答を待っていますが、時間がかかり、ユーザーが他のUIに移動する可能性があります。この待機中のスレッドは現在、a)役に立たないb)潜在的な問題です。彼が結果を得るとき、それは完全に役に立たず、エラーの数につながるコールバックをトリガーするからです。

これらすべてと、CPUに負荷がかかる可能性のある応答処理を実行できます。またif (Thread.currentThread().isInterrupted())、すべてのコードで行をスローすることはできないため、開発者はそれを止めることもできません。

したがって、奇妙なスレッドを強制的に停止することができません。


ネットワーク操作がすでに安全に中止できる場合は、スレッドを中断します。ネットワーク操作が安全に中止できない場合、Thread.stop()とにかく安全に呼び出すことができません。に投票するのではなくThread.stop()、安全に中止できるようになるまでに時間がかかる可能性のあるすべての操作を実装するすべての人を求めています。そしてそれは良い考えかもしれませんがThread.stop()、安全な中絶を要求する方法として実装することとは何の関係もありません。私たちはすでにinterruptそのために持っています。
David Schwartz、

「そのため、奇妙なスレッドを強制的に停止できない。」-...(Javaデザイナーが行ったように)深く見て、非推奨よりも悪くない技術的に実行可能なソリューションはないと結論するまでstop
スティーブンC

5

質問はかなりあいまいです。「必要なときにスレッドが実行を停止するようにプログラムを作成する方法」を意味している場合は、他のさまざまな応答が役立つはずです。しかし、「サーバーに緊急事態が発生したため、現在再起動できず、特定のスレッドが必要な場合にのみ、死ぬ可能性があります」という場合、のような監視ツールと一致する介入ツールが必要ですjstack

この目的のために、私はjkillthreadを作成しました。使用方法については、その手順を参照してください。


ありがとうございました!まさに私が探していたもの!
Denis Kokorin

4

もちろん、完全に信頼されていない何らかのコードを実行している場合もあります。(私は自分のJava環境でアップロードされたスクリプトの実行を許可することでこれを個人的に行っています。はい、どこでもセキュリティアラームのベルが鳴っていますが、それはアプリケーションの一部です。)ある種のブール実行/非実行信号を尊重します。あなたの唯一のまともなフェイルセーフは、たとえば、あるタイムアウトよりも長く実行された場合に、スレッドのstopメソッドを呼び出すことです。

ただし、これは「まともな」ものであり、絶対的なものではありません。コードがThreadDeathエラー(または明示的にスローした例外)をキャッチし、紳士的なスレッドが行うはずのように再スローしないためです。つまり、最終的にはAFAIAであり、絶対的なフェイルセーフはありません。


私の知る限り、ますます多くのサービスがハイブリッドになり、管理された環境を使用してサードパーティのコード(プラグイン、スクリプトなど)を実行します。これらのコードは、コードを完全に制御できず、スレッドを完全に削除するのは無理のようです。サービスエンジニアにとって、ライブアンドサービング状態は、非サービング状態(ハング(スレッドを奪う)またはビジー無限ループ(コアを奪う)により)よりもはるかに優れている可能性があるため
Weipeng L

3

スレッドを適切に強制終了する方法はありません。

スレッドを中断しようとすることができます。一般的な戦略の1つは、毒薬を使用してスレッドにメッセージを送り、それ自体を停止することです

public class CancelSupport {
    public static class CommandExecutor implements Runnable {
            private BlockingQueue<String> queue;
            public static final String POISON_PILL  = stopnow”;
            public CommandExecutor(BlockingQueue<String> queue) {
                    this.queue=queue;
            }
            @Override
            public void run() {
                    boolean stop=false;
                    while(!stop) {
                            try {
                                    String command=queue.take();
                                    if(POISON_PILL.equals(command)) {
                                            stop=true;
                                    } else {
                                            // do command
                                            System.out.println(command);
                                    }
                            } catch (InterruptedException e) {
                                    stop=true;
                            }
                    }
                    System.out.println(“Stopping execution”);
            }

    }

}

BlockingQueue<String> queue=new LinkedBlockingQueue<String>();
Thread t=new Thread(new CommandExecutor(queue));
queue.put(“hello”);
queue.put(“world”);
t.start();
Thread.sleep(1000);
queue.put(“stopnow”);

http://anandsekar.github.io/cancel-support-for-threads/


2

通常、スレッドを強制終了、停止、または中断することはせず(または、interrupted()であるかどうかを確認します)、自然に終了させます。

簡単です。ループをrun()メソッド内で(揮発性)ブール変数と一緒に使用して、スレッドのアクティビティを制御できます。アクティブスレッドからメインスレッドに戻って停止することもできます。

このようにして、スレッドを優雅に削除します:)。


1

突然のスレッド終了の試みは、よく知られた悪いプログラミング慣行であり、アプリケーション設計が不十分であることの証拠です。マルチスレッドアプリケーションのすべてのスレッドは、明示的および暗黙的に同じプロセス状態を共有し、一貫性を保つために相互に協力することを強制されます。したがって、慎重かつ明確なアプリケーション設計を通じてそのような一貫性の保証を提供することは、開発者の責任です。

制御されたスレッドの終了には、主に2つの正しい解決策があります。

  • 共有揮発性フラグの使用
  • Thread.interrupt()メソッドとThread.interrupted()メソッドのペアの使用。

突然のスレッドの終了に関連する問題の適切で詳細な説明と、制御されたスレッドの終了の間違った正しい解決策の例は、次の場所にあります。

https://www.securecoding.cert.org/confluence/display/java/THI05-J.+Do+not+use+Thread.stop%28%29+to+terminate+threads


プログラムは1人の開発者によって作成されなくなったため、スレッドの強制終了が必要になることがよくあります。したがって、悪いプログラミングと見なすことはできません。Linuxを殺さずには想像できません。スレッドを強制終了できないことは、Javaの欠陥です。
Pavel Niedoba 2017年

1
すばらしいですね。サードパーティのライブラリを呼び出す必要がある場合はどうですか。制御できない、タイムアウトのバグがあり、実行時に1000〜2000回に1回ハングする可能性があります。悪いプログラミング練習だよね?使用しているコードに常にアクセスできるわけではありません。とにかく、opは、独自のコードを設計するときに、フローを制御する方法ではなく、スレッドを強制終了する方法を尋ねています...
Arturas M

問題は、ミューテックスを所有している、またはメモリブロックが割り当てられている、または他のスレッドが待機しているイベントまたはデータを生成するスレッドを強制終了するとどうなるかです。アプリケーションの残りのロジックはどうなりますか?小さくて明白な問題が複雑な問題に変換され、再現や調査が困難になるリスクが常にあります。スレッドを強制終了すると、アプリケーションがさまざまな矛盾した状態になる可能性があるため、安全ではありません。cert.orgのリンクから情報を見てください。
ZarathustrA

特定の条件で、計算全体が無意味になったと判断する制御スレッドがあります。実際の作業を行うさまざまなランナブルは、便利な場所でisInterrupted()のバリアントで飛び散る必要があります。コントローラーがThreadDeathを一見どこからともなく立ち上げて、すべての同期されたブロックと最終的にブロックをきれいにほどくと、それはとても良いことです。誰もがこれはエラーが発生しやすいと言っていますが、かなり関連のないスレッドの場合、その理由はわかりません。
Daniel


1

私はAndroidで割り込みを機能させなかったので、この方法を使用して完全に動作しました。

boolean shouldCheckUpdates = true;

private void startupCheckForUpdatesEveryFewSeconds() {
    Thread t = new Thread(new CheckUpdates());
    t.start();
}

private class CheckUpdates implements Runnable{
    public void run() {
        while (shouldCheckUpdates){
            //Thread sleep 3 seconds
            System.out.println("Do your thing here");
        }
    }
}

 public void stop(){
        shouldCheckUpdates = false;
 }

2
コンパイラがスレッドローカルストレージで最適化する場合は、volatileキーワードをshouldCheckUpdatesに追加する必要があります。
clinux 2018

1

「スレッドを強制終了する」は適切なフレーズではありません。これは、スレッドの正常な完了/終了を自由に実装できる1つの方法です。

私が使用したRunnable:

class TaskThread implements Runnable {

    boolean shouldStop;

    public TaskThread(boolean shouldStop) {
        this.shouldStop = shouldStop;
    }

    @Override
    public void run() {

        System.out.println("Thread has started");

        while (!shouldStop) {
            // do something
        }

        System.out.println("Thread has ended");

    }

    public void stop() {
        shouldStop = true;
    }

}

トリガークラス:

public class ThreadStop {

    public static void main(String[] args) {

        System.out.println("Start");

        // Start the thread
        TaskThread task = new TaskThread(false);
        Thread t = new Thread(task);
        t.start();

        // Stop the thread
        task.stop();

        System.out.println("End");

    }

}

コンパイラがスレッドローカルストレージで最適化する場合に備えて、volatileキーワードをshouldStop変数に追加する必要があります。
Oleksii Kyslytsyn

0

Thread.stopは非推奨です。Javaでスレッドを停止するにはどうすればよいですか?

常に割り込みメソッドとフューチャーを使用してキャンセルをリクエストする

  1. たとえば、タスクが割り込み信号に応答すると、ブロッキングキューテイクメソッドがブロックされます。
Callable < String > callable = new Callable < String > () {
    @Override
    public String call() throws Exception {
        String result = "";
        try {
            //assume below take method is blocked as no work is produced.
            result = queue.take();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return result;
    }
};
Future future = executor.submit(callable);
try {
    String result = future.get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
    logger.error("Thread timedout!");
    return "";
} finally {
    //this will call interrupt on queue which will abort the operation.
    //if it completes before time out, it has no side effects
    future.cancel(true);
}
  1. タスクが割り込み信号に応答しない場合、タスクが割り込み信号に応答しないソケットI / Oを実行し、上記のアプローチを使用してもタスクが中止されないと仮定すると、将来タイムアウトしますが、最終的にブロックをキャンセルしても効果がありません。 、スレッドはソケットをリッスンし続けます。プールで実装されている場合は、ソケットを閉じるか、接続時にcloseメソッドを呼び出すことができます。
public interface CustomCallable < T > extends Callable < T > {
    void cancel();
    RunnableFuture < T > newTask();
}

public class CustomExecutorPool extends ThreadPoolExecutor {
    protected < T > RunnableFuture < T > newTaskFor(Callable < T > callable) {
        if (callable instanceof CancellableTask)
            return ((CancellableTask < T > ) callable).newTask();
        else
            return super.newTaskFor(callable);
    }
}

public abstract class UnblockingIOTask < T > implements CustomCallable < T > {
    public synchronized void cancel() {
        try {
            obj.close();
        } catch (IOException e) {
            logger.error("io exception", e);
        }
    }

    public RunnableFuture < T > newTask() {
        return new FutureTask < T > (this) {
            public boolean cancel(boolean mayInterruptIfRunning) {
                try {
                    this.cancel();
                } finally {
                    return super.cancel(mayInterruptIfRunning);
                }
            }

        };
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.