Struts ScopeInterceptor类中的Java线程安全问题?

时间:2013-09-24 14:57:00

标签: java struts2 thread-safety hashmap

我试图了解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()包装器是正确的修复程序,还是我遗漏了什么?

3 个答案:

答案 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来解决。