30

今日、非常に驚​​くべき例外に遭遇しました。同期ブロック内で、wait() を呼び出すと、 がスローされIllegalMonitorStateExceptionます。何が原因でしょうか?

これは、十分にテストされたオープン ソース コードで発生しています 。 #l222

明らかな原因を排除しました。

  • 正しい変数で同期されていますか? はい、そうですmuxLock
  • それは可変変数ですか?いいえ、muxLock最終です
  • モニターの動作に影響を与える可能性のある奇妙な「-XX:」JVM フラグを使用していませんか? いいえ。ただし、JNI を介して C++ アプリ内に埋め込まれた JVM を起動しています。
  • これは奇妙な JVM ですか? いいえ、Sun の 1.6.0_25 win/x64 JRE です。
  • これは既知の JVM バグですか? http://bugs.sun.com/bugdatabaseで関連するものが見つかりません

だから、私はもっととてつもない説明を考えようとしています。

  • キャッチされていないメモリ不足エラーにより、モニターの状態が台無しになる可能性はありますか? これを確認していますが、メモリ エラーの兆候はまだ見られません。

更新:(コメントに基づく)

また、スタックトレースとブレークポイントから、例外がスローされたときにスレッドが実際に同期ブロック内にあることを確認しました。他の無関係なコードが例外を発行しているわけではありません (何かが Eclipse を本当に混乱させない限り!)

4

4 に答える 4

6

私が見る唯一の疑わしいことは、「this」への参照をコンストラクターの他のオブジェクトに渡していることです。他のスレッドが「this」への参照を取得し、muxlock を使用するメソッドを呼び出すと、物事の奇妙な再順序付けによって、物事が非常にうまくいかない可能性はありますか (実際にはありそうにありません)。

Java 言語仕様は、これについてかなり具体的です。

コンストラクターが終了すると、オブジェクトは完全に初期化されたと見なされます。オブジェクトが完全に初期化された後にのみオブジェクトへの参照を確認できるスレッドは、そのオブジェクトの最終フィールドの正しく初期化された値を確認できることが保証されます。

つまり、コンストラクターが終了する前に別のスレッドが「this」参照を取得すると、最終フィールド「muxlock」がまだ正しく初期化されていない可能性があります。一般に、コンストラクターが終了する前に 'this' への参照を公開することは、特にスレッド化された状況では非常に危険です。

そのようなことについての潜在的に役立つ議論: http://madpropellerhead.com/random/20100328-java-final-fields-are-not-as-final-as-you-may-think

コンストラクターで「this」を発行することが一般的に非常に悪い考えである理由についての、以前の、しかしまだ有用な一般的な議論については、たとえば、 http ://www.ibm.com/developerworks/java/library/j-jtp0618/ index.html

于 2011-09-28T12:37:03.007 に答える
2

http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?r1=1069292&r2=1135026&diff_format=h

ここで、タイムアウトが最近追加されたことがわかります

startTimeout が 0 より大きいことを確認してください。それ以外の場合は、wait(0) または wait(-n) になります。これにより、IllegalMonitorStateException が発生する可能性があります。

編集:上記は災害ですが、これを試してみましょう:

Mux コンストラクターにいます: http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?view=markup

176 行目で SocketChannelConnectionIO を作成し、これを渡します。その後、ブレークし、別のスレッドが引き継ぎます。

ここで定義されている SocketChannelConnectionIO のコンストラクター: http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/SocketChannelConnectionIO.java?view=markup 行 112 を登録します。 new handler() でチャネリングします。

ハンドラーはシャネルで何かを受け取り、関数は、関数 handleReadReady が実行され、 muxLock で同期するとします。

今はまだコンストラクターにいるので、ファイナルのオブジェクトはまだ変更可能です!!! それが変更されたと仮定しましょう。今、別のmuxLockで何かを待っています

100万分の1のシナリオ

編集

http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?revision=1135026&view=co

Mux(SocketChannel channel,
    int role, int initialInboundRation, int maxFragmentSize)
    throws IOException
    {
    this.role = role;
    if ((initialInboundRation & ~0x00FFFF00) != 0) {
        throw new IllegalArgumentException(
        "illegal initial inbound ration: " +
        toHexString(initialInboundRation));
    }
    this.initialInboundRation = initialInboundRation;
    this.maxFragmentSize = maxFragmentSize;

    //LINE BELOW IS CAUSING PROBLEM it passes this to SocketChannelConnectionIO
    this.connectionIO = new SocketChannelConnectionIO(this, channel);

    //Lets assume it stops here we are still in constructor
    //and we are not in synchronized block

    directBuffersUseful = true;
    }

現在、SocketChannelConnectionIO http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/SocketChannelConnectionIO.java?revision=1069292&view=coのコンストラクタにあります。

SocketChannelConnectionIO(Mux mux, SocketChannel channel)
    throws IOException
{
    super(mux);
    channel.configureBlocking(false);
    this.channel = channel;
    //Line below we are registering to the channel with mux that is still mutable
    //this is the line that actually is causing the problem move that to 
    // start() and it should work 
    key = selectionManager.register(channel, new Handler());
}

このコードを start() に移動すると動作するはずkey = selectionManager.register(channel, new Handler());です (処理を開始したい場合は start が executet であると想定しています)

/**
 * Starts processing connection data.
 */
void start() throws IOException {
    key = selectionManager.register(channel, new Handler());
    key.renewInterestMask(SelectionKey.OP_READ);
}

ただし、mux のコンストラクターで SocketChannelConnectionIO を作成しない方がはるかに良いでしょうが、その後のどこかで、これで StreamConnectionIO を作成する 2 番目のコンストラクターと同じになる可能性があります。

于 2011-09-29T17:25:48.137 に答える
1

メンバー変数は、期待するほど最終的ではありません。最初に、同期されたオブジェクトを最終的なローカル変数に入れる必要があります。これはメンバー変数が変更された理由を説明していませんが、問題が修正された場合、少なくともメンバー変数が実際に変更されていることがわかります。

于 2011-09-29T16:25:33.020 に答える
1

答えは、私の意見では、バグであるか、参照の背後にあるオブジェクトが最終的であるにもかかわらず誰かが変更したかのいずれかです。再現できる場合は、muxlock フィールドに読み取り/書き込みブレークポイントを設定して、触れられているかどうかを確認することをお勧めします。同期ブロックの最初の行で、適切なログ エントリまたはブレークポイントで待機および通知する前に、muxlock の ID ハッシュコードを確認できます。リフレクションを使用すると、最終参照を変更できます。http://download.oracle.com/javase/6/docs/api/java/lang/reflect/Field.htmlからの引用:

"基になるフィールドが final の場合、setAccessible(true) がこのフィールドで成功し、このフィールドが非静的でない限り、メソッドは IllegalAccessException をスローますプログラムの他の部分からアクセスできるようになる前に、最後のフィールドを空白にします。他のコンテキストで使用すると、プログラムの他の部分がこのフィールドの元の値を使用し続ける場合を含め、予測できない影響を与える可能性があります。」

おそらくEclipseのバグであり、デバッグ中にフィールドが何らかの形で変更されます。Eclipse以外でも再現可能ですか?printstractrace を catch に入れて、何が起こるか見てみましょう。

于 2011-09-23T21:11:44.310 に答える