3

Struts2 ScopeInterceptor クラス (/org/apache/struts2/interceptor/ScopeInterceptor.java) 内にスレッド セーフの問題があるかどうかを理解しようとしています。問題のコードは次のとおりです。

    private static Map locks = new IdentityHashMap();

static final void lock(Object o, ActionInvocation invocation) throws Exception {
    synchronized (o) {
        int count = 3;
        Object previous = null;
        while ((previous = locks.get(o)) != null) {
            if (previous == invocation) {
                return;
            }
            if (count-- <= 0) {
                locks.remove(o);
                o.notify();

                throw new StrutsException("Deadlock in session lock");
            }
            o.wait(10000);
        }
        ;
        locks.put(o, invocation);
    }
}

static final void unlock(Object o) {
    synchronized (o) {
        locks.remove(o);
        o.notify();
    }
}

45 個のスレッドが停止し、CPU 使用率が高い Websphere アプリケーションがあります。「unlock」メソッド内の「locks.remove(o)」で 33 のスレッドが停止します。他の 12 のスレッドは、「lock」メソッド内の「locks.get(o)」内で停止します。

IdentityHashMap の使用はスレッドセーフではないように思えます。Collections.synchronizedMap() で IdentityHashMap をラップするだけで、この問題を解決できますか?:

    private static Map locks = Collections.synchronizedMap(new IdentityHashMap());

static final void lock(Object o, ActionInvocation invocation) throws Exception {
    synchronized (o) {
        int count = 3;
        Object previous = null;
        while ((previous = locks.get(o)) != null) {
            if (previous == invocation) {
                return;
            }
            if (count-- <= 0) {
                locks.remove(o);
                o.notify();

                throw new StrutsException("Deadlock in session lock");
            }
            o.wait(10000);
        }
        ;
        locks.put(o, invocation);
    }
}

static final void unlock(Object o) {
    synchronized (o) {
        locks.remove(o);
        o.notify();
    }
}

作成者は、同期されたコード ブロックを使用して IdentityHashMap の同期の問題を「修正」しようとしたようですが、オブジェクト「o」がスレッド固有のオブジェクトである場合、複数のスレッドから保護されません。そして、lock と unlock 内のコード ブロックは分離されているため、IdentityHashMap は複数のスレッドから同時に呼び出されます (そして実際に呼び出されます!) (Java コアの証拠によると)。

Collections.synchronizedMap() ラッパーは正しい修正ですか、それとも何か不足していますか?

4

3 に答える 3

0

はい、そう思います。異なる os で lock(Object o, ActionInvocation invocation) にアクセスする場合、異なるスレッドの異なるモニターで同時に IdentityHashMap を変更します。これにより、異なるスレッドが IdentityHashMap を同時に呼び出すことができます。

これは、IdentityHashMap を同期することで解決できます。

于 2013-09-28T19:22:54.840 に答える
0

本当の答えはありませんが、役立つ情報があれば幸いです。

IdentityHashMap のドキュメント ( http://docs.oracle.com/javase/7/docs/api/java/util/IdentityHashMap.html ) には次のように記載されています。

この実装は同期されていないことに注意してください。複数のスレッドが ID ハッシュ マップに同時にアクセスし、少なくとも 1 つのスレッドがマップを構造的に変更する場合は、外部で同期する必要があります。(構造変更とは、1 つ以上のマッピングを追加または削除する操作です。インスタンスに既に含まれているキーに関連付けられた値を単に変更するだけでは、構造変更ではありません。)これは通常、マップを自然にカプセル化するオブジェクトを同期することによって達成されます。 . そのようなオブジェクトが存在しない場合は、Collections.synchronizedMap メソッドを使用してマップを「ラップ」する必要があります。これは、マップへの偶発的な非同期アクセスを防ぐために、作成時に行うのが最適です。

Map m = Collections.synchronizedMap(new IdentityHashMap(...));

したがって、 Collections.synchronizedMap 戦略は正しいように聞こえますが、このページ ( http://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html ) では、メソッドが静的であるため、機能するかどうか疑問に思います。

同期されたメソッドのロック

スレッドが同期メソッドを呼び出すと、そのメソッドのオブジェクトの固有ロックが自動的に取得され、メソッドが戻るときに解放されます。キャッチされなかった例外が原因で返された場合でも、ロックの解放が行われます。

静的メソッドはオブジェクトではなくクラスに関連付けられているため、静的同期メソッドが呼び出されるとどうなるか疑問に思うかもしれません。この場合、スレッドは、クラスに関連付けられた Class オブジェクトの固有ロックを取得します。したがって、クラスの静的フィールドへのアクセスは、クラスのインスタンスのロックとは異なるロックによって制御されます。

これらは静的メソッドであるため (フィールドは静的ではありませんが)、 Collections.synchronizedMap ラッパーがデッドロックを防ぐために本当に機能するかどうかを判断するのは困難です...私の答えは、答えがないということです!

于 2013-09-25T03:06:23.147 に答える