我试图了解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();
}
}
我有一个Websphere应用程序显示45个停滞的线程,高CPU使用率。 33个线程在“unlock”方法内的“locks.remove(o)”处停顿。其他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的同步问题,但是如果Object“o”是特定于线程的对象,则不能防止多个线程。并且,由于锁定和解锁中的代码块是分开的,因此IdentityHashMap将(并且确实!)由多个线程同时调用(根据我们的Java核心证据)。
Collections.synchronizedMap()包装器是正确的修复程序,还是我遗漏了什么?
答案 0 :(得分:0)
没有真正的答案,但希望有一些有用的信息:
IdentityHashMap文档(http://docs.oracle.com/javase/7/docs/api/java/util/IdentityHashMap.html)声明:
请注意,此实施未同步。如果多个线程同时访问身份哈希映射,并且至少有一个 线程在结构上修改地图,必须同步 外部。 (结构修改是添加或的任何操作 删除一个或多个映射;只是改变相关的价值 使用实例已包含的键不是结构 修改。)这通常通过同步一些来完成 自然封装地图的对象。如果不存在这样的对象, 应使用Collections.synchronizedMap“包装”地图 方法。这最好在创建时完成,以防止意外 对地图的非同步访问:
Map m = Collections.synchronizedMap(new IdentityHashMap(...));
所以Collections.synchronizedMap策略听起来是正确的,但是这个页面(http://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html)让我想知道它是否会起作用,因为这些方法是静态的:
锁定同步方法
当线程调用synchronized方法时,它会自动获取 该方法的对象的内在锁定,并在该方法时释放它 方法返回。即使返回发生,也会发生锁定释放 一个未被捕获的例外。
您可能想知道静态同步方法会发生什么 调用,因为静态方法与类关联,而不是 宾语。在这种情况下,线程获取内部锁 与类关联的类对象。从而访问类的静态 字段由锁定控制,该锁定与任何锁定不同 班级的实例。
由于这些是静态方法(即使字段不是静态的),很难判断Collections.synchronizedMap包装器是否真的可以防止死锁...我的答案是我没有答案!
答案 1 :(得分:0)
我相信你是对的,似乎存在线程安全问题。开发人员试图通过同步对象“o”来保证线程安全,但看起来这个对象实际上是会话对象而不是更广泛的范围。我认为更改需要在锁定对象上进行同步。
答案 2 :(得分:0)
是的,我想是的。 如果您使用不同的os访问lock(Object o,ActionInvocation调用),则会使用不同的监视器同时修改IdentityHashMap以用于不同的线程。这使得不同的线程可以同时调用IdentityHashMap。
这可以通过同步IdentityHashMap来解决。