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() ラッパーは正しい修正ですか、それとも何か不足していますか?