当前位置: 首页 > 知识库问答 >
问题:

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

甄成弘
2023-03-14

我试图了解是否有一个线程安全问题内部的Struts2范围拦截类(/org/apache/struts2/拦截器/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个线程在“解锁”方法内部的“locks.remove(o)”处停滞。其他12个线程在“锁定”方法内部的“locks.get(o)”处停滞。

在我看来,IdentityHashMap的使用是线程不安全的。可以简单地用集合包装IdentityHashMap。synchronizedMap()解决此问题?:

    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”是特定于线程的对象,则无法防止多个线程。而且,由于锁定和解锁中的代码块是分开的,因此IdentityHashMap将(并且确实!)被多个线程同时调用(根据我们的Java核心证据)。

是集合。synchronizedMap()包装了正确的修复,还是我遗漏了什么?

共有3个答案

卜勇
2023-03-14

是的,我想是的。如果你用不同的操作系统访问lock(Object o,ActionInvocation invocation ),你就同时用不同的监视器为不同的线程修改了IdentityHashMap。这使得不同的线程可以同时调用IdentityHashMap。

这可以通过同步IdentityHashMap来解决。

靳金鹏
2023-03-14

没有真正的答案,但希望有一些有用的信息:

IdentityHashMap文档(http://docs.oracle.com/javase/7/docs/api/java/util/IdentityHashMap.html)状态:

注意,这个实现是不同步的。如果多个线程同时访问一个身份哈希映射,并且至少有一个线程在结构上修改了该映射,则必须在外部对其进行同步。(结构修改是添加或删除一个或多个映射的任何操作;仅仅改变与实例已经包含的键相关联的值不是结构修改。)这通常是通过对一些自然封装地图的对象进行同步来实现的。如果不存在这样的对象,应该使用Collections.synchronizedMap方法“包装”该映射。这最好在创建时完成,以防止意外的对地图的不同步访问:

Map m=Collections.synchronizedMap(新标识HashMap(…));

因此Collections.synchronizedMap策略听起来是对的,但是这个页面(http://docs . Oracle . com/javase/tutorial/essential/concurrency/lock sync . html)让我怀疑它是否可行,因为这些方法是静态的:

同步方法中的锁

当线程调用同步方法时,它会自动获取该方法对象的内部锁,并在方法返回时释放该锁。即使返回是由未捕获的异常引起的,也会发生锁释放。

您可能想知道调用静态同步方法时会发生什么,因为静态方法与类相关联,而不是与对象相关联。在这种情况下,线程获取与该类相关联的类对象的固有锁。因此,对类的静态字段的访问是由一个锁控制的,这个锁不同于该类的任何实例的锁。

因为这些是静态方法(即使字段不是静态的),所以很难判断Collections.synchronizedMap包装器是否真的能防止死锁...我的回答是我没有答案!

潘飞英
2023-03-14

我相信你是对的,这似乎是一个线程安全问题。开发人员试图通过对对象“o”进行同步来实现线程安全,但看起来该对象实际上是会话对象,而不是范围更广的对象。我认为更改需要在锁对象上同步。

 类似资料:
  • 我找到了关于线程安全的代码,但它没有来自给出示例的人的任何解释。我想知道为什么如果我不在“count”之前设置“synchronized”变量,那么count值将是非原子的(总是=200是期望的结果)。谢谢

  • 问题内容: 我写了下面的Singleton类。我不确定这是否是线程安全的单例类吗? 谁能帮我这个?我在上述Singleton课堂上的任何想法都会有很大帮助。 更新代码: 我正在尝试将波希米亚建议纳入我的代码中。这是更新的代码,我得到了- 谁能看一下,让我知道这次是否正确? 问题答案: 但是有一个简单的技巧,可以让你的代码是线程安全的实现,并不需要同步!它称为“ 按需初始化持有人”习惯用法,它看起来

  • 我有一个应用程序,它有一个ConcurrentHashMap本地存储一个存储在外部服务器上的数据副本。地图每隔几秒钟就会更新一次数据的新副本。 我有一个循环,每隔几秒钟运行一次,它可以访问HashMap并按照值的顺序将元素添加到数组中(实际上它做的事情还多一些,但这并不相关)。我的问题是,如果数据在创建数组的过程中发生了变化,您可能会在不同的地方有重复的键,或者完全省略一些键。 示例: 如您所见,

  • 问题内容: 经过一番认真的搜索后,我发现RandomAccessFile- class不是线程安全的。现在,我可以使用一个信号量来锁定所有读取和写入,但是我认为这样做的效果不是很好。从理论上讲,一次可以进行多次读取和一次写入。如何用Java做到这一点?有可能吗? 谢谢! 问题答案: 文件的部分锁定是一项复杂的业务,许多操作系统都避免这样做。但是,如果您坚持要这样做,一种方法是设计自己的锁定机制对象

  • 问题内容: 有关Singletons的维基百科文章提到了一些用线程安全的方法来用Java实现结构。对于我的问题,让我们考虑具有冗长的初始化过程并且一次被多个线程访问的Singleton。 首先,这个未提及的方法是线程安全的吗?如果是的话,它在什么上进行同步? 其次,为什么以下实现线程安全且在初始化时是懒惰的?如果两个线程同时进入该方法,到底会发生什么? 最后,在第二个示例中,如果一个线程首先获取一

  • 问题内容: 鉴于以下多态: 我们如何在没有昂贵的getInstance()方法同步和双重检查锁定争议的情况下使它保持线程安全和懒惰?这里提到了单例的有效方法,但似乎并没有扩展到多例。 问题答案: 使用Java 8,它甚至可以更简单: