20

プログラムがハングし、デッドロックのように見える状況に遭遇しました。しかし、jconsoleとvisualvmを使用してそれを理解しようとしましたが、デッドロックは検出されませんでした。サンプルコード:

public class StaticInitializer {

private static int state = 10;

static {
    Thread t1 = new Thread(new Runnable() {
        @Override
        public void run() {
            state = 11;
            System.out.println("Exit Thread");
        }
    });

    t1.start();

    try {
        t1.join();
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    System.out.println("exiting static block");
}

public static void main(String...strings) {
    System.out.println(state);
}
}

これをデバッグモードで実行すると、コントロールが@Override public void run(){state = 11;

ただし、state = 11が実行されるとすぐに、ハング/デッドロックが発生します。私はstackoverflowのさまざまな投稿を調べ、静的初期化子はスレッドセーフだと思いましたが、その場合、jconsoleはこれを報告する必要があります。メインスレッドについて、jconsoleは待機状態にあると言っていますが、それで問題ありません。ただし、静的初期化子ブロックで作成されたスレッドの場合、jconsoleは、スレッドがRUNNABLE状態であり、ブロックされていないと言います。私は混乱していて、ここではいくつかの概念が欠けています。私を助けてください。

4

4 に答える 4

35

あなたは別のスレッドを開始しているのではなく、それに参加しています。その新しいスレッドはStaticInitializer、フィールドを設定しようとしているため、続行する前に完全に初期化されるまで待機する必要がありstateます...そして初期化はすでに進行中であるため、待機します。ただし、その初期化はその新しいスレッドが終了するのを待っているため、永遠に待機することになります。古典的なデッドロック。

クラスの初期化に関する詳細については、Java 言語仕様セクション 12.4.2を参照してください。重要なことに、初期化スレッドは のモニターを「所有」しますStaticInitializer.classが、新しいスレッドはそのモニターの取得を待機します。

つまり、コードはこの非初期化コードに少し似ています (例外処理が省略されています)。

final Object foo = new Object();
synchronized (foo)
{
    Thread t1 = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized (foo) {
                System.out.println("In the new thread!");
            }
        });
    t1.start();
    t1.join();
});

そのコードがデッドロックする理由を理解できれば、それは基本的にあなたのコードでも同じです。

道徳は、静的初期化子で多くの作業を行うことではありません。

于 2011-09-22T16:18:39.130 に答える
13

クラスローディングは、jvm では微妙な時間のようなものです。クラスが初期化されると、同じクラスを操作しようとする他のスレッドを一時停止する内部 jvm ロックが保持されます。そのため、生成されたスレッドは、処理を進める前に StaticInitializer クラスが完全に初期化されるのを待っている可能性があります。ただし、 StaticInitializer クラスは、完全に初期化される前にスレッドが完了するのを待っています。したがって、デッドロック。

もちろん、本当にこのようなことをしようとしている場合は、セカンダリ スレッドを開始した直後に参加するため、セカンダリ スレッドは不要です (したがって、そのコードを直接実行することもできます)。

アップデート:

デッドロックが検出されない理由についての私の推測は、標準のデッドロック検出コードが機能するレベルよりもはるかに低いレベルで発生しているためです。そのコードは通常のオブジェクト ロックで動作しますが、これは深い jvm 内部のものです。

于 2011-09-22T16:16:01.467 に答える
5

行をコメントアウトすることで、プログラムを実行できましたstate = 11;

初期化が完了するまで、state=11 を設定することはできません。t1 の実行が終了するまで、初期化を完了できません。state=11 に設定するまで、T1 は実行を終了できません。デッドロック。

于 2011-09-22T16:17:30.600 に答える
4

これが私が思うことです:

  1. メインスレッドは初期化を試みますStaticInitializerClassこれには、対応するオブジェクトのロックが含まれます。
  2. ロックを保持している間、メイン スレッドは別のスレッドを生成し、それが終了するのを待ちます
  3. もう一方のスレッドのrun()メソッドは、完全に初期化stateする必要がある にアクセスしようとします。StaticInitializerこれには、ステップ 1 と同じロックでの待機が含まれます。

最終結果: デッドロック。

初期化手順の詳細については、JLS を参照してください。

に移動t1.join()するmain()と、すべてが機能します。

于 2011-09-22T16:23:36.967 に答える