Java6同步中的变化?

Java6同步中的变化?,java,multithreading,jvm,synchronization,jdk1.6,Java,Multithreading,Jvm,Synchronization,Jdk1.6,我正在看一些在Java6和更高版本中导致问题(死锁)的代码,但在Java1.5中没有 BMP Bean: private MyClass m_c; public String ejbCreate(String id) throws CreateException, MyException { try { m_c = Singleton.getInstance().getObj(id); } catch (MyException e) {

我正在看一些在Java6和更高版本中导致问题(死锁)的代码,但在Java1.5中没有

BMP Bean:

private MyClass m_c;
public String ejbCreate(String id) throws CreateException, MyException
{
    try
    {
        m_c = Singleton.getInstance().getObj(id);
    }
    catch (MyException e)
    {
        synchronized (Singleton.getInstance())
        {
            //check again
            if (!Singleton.getInstance().hasObj(id)) {
                m_c = new MyClass(id);
                Singleton.getInstance().addObj(id, m_c);
            }
            else {
                m_c = Singleton.getInstance().getObj(id);
            }
        }
    }
}
单身人士:

private Map objCache = new HashMap();
private static Singleton INSTANCE = new Singleton();
public static Singleton getInstance() {
    return INSTANCE;
}
public void addObj(String id, MyClass o)
{
    if (this.objCache.containsKey(id)) {
        this.objCache.remove(id);
    }
    this.objCache.put(id, o);
}

public MyClass getObj(String id) throws Exception
{
    MyClass o = null;
    o = (MyClass)this.objCache.get(id);
    if (o == null) {
        throw new MyException("Obj " +id+ " not found in cache");
    }
    return o;
}

public boolean hasObj(String id)
{
    return this.objCache.containsKey(id);
}
迄今为止的经验证据表明,在使用Java6时,将同步放在整个try/catch中可以解决死锁

显然,可以有一个或多个线程调用

Singleton.getInstance().getObj(id) 
当另一个线程拥有锁并在同步块中执行代码时,没有获得锁,但即使考虑了JSR-133中详细介绍的内存同步,在这种情况下看起来也不应该有任何问题

我知道,除了说这是一个僵局之外,我还没有解释这个问题是什么,只画一张图片并不理想,但画整个图片需要很大的画布

我已经看过了Java6发行版的notes,听起来唯一相关的领域是无争用同步,但我不知道这在本例中是否重要


谢谢您的帮助。

我怀疑您并没有遇到死锁(在以不同顺序获得的两个不同线程中持有两个锁),而是进入了一个无限循环。如果您以非线程安全的方式访问HashMap,则可能会发生这种情况。在用来处理冲突的链表中发生的事情似乎会自动返回,读卡器将永远运行。这一直是一个问题,尽管Java6中的一些细微差异可能会在不同版本可能不存在的情况下显示出这个问题

我建议您修复这个类,使它使用线程安全的集合,而不是在异常时重试,因为不能保证会发生这种情况

您可以做很多事情来改进这个类,但真正需要的是Java 8中添加的ConcurrentMap.ComputeFabSent

注意:没有理由这样做

  • 在尝试删除密钥之前,请检查该密钥是否存在
  • 在尝试放置密钥之前,请将其移除
  • 抛出异常而不是返回null
  • 当您可以将其传递给工厂时返回null。(根据已发送的计算机)
  • 如果事先知道类型,请使用工厂
我建议你

  • 使用ConcurrentMap进行线程安全并发访问
  • 对单例使用
    enum
这两个都是在Java5.0中添加的

public enum MyClassCache {
    INSTANCE;

    private final Map<String, MyClass> cache = new ConcurrentHashMap<>();

    public boolean hasId(String id) {
        return cache.containsKey(id);
    }

    public MyClass get(String id) throws IllegalStateException {
        MyClass ret = cache.get(id);
        if (ret == null) throw new IllegalStateException(id);
        return ret;
    }

    public MyClass getOrCreate(String id) throws IllegalStateException {
        MyClass ret = cache.get(id);
        if (ret == null) {
            synchronized (cache) {
                ret = cache.get(id);
                if (ret == null) {
                    cache.put(id, ret = new MyClass(id));
                }
            }
        }
        return ret;
    }
}

我说的对吗?这个问题的核心是:

public void ejbCreate1(String id) throws Exception {
    try {
        m_c = Singleton.getInstance().getObj(id);
    } catch (Exception e) {
        synchronized (Singleton.getInstance()) {
            //check again
            if (!Singleton.getInstance().hasObj(id)) {
                m_c = new MyClass(id);
                Singleton.getInstance().addObj(id, m_c);
            } else {
                m_c = Singleton.getInstance().getObj(id);
            }
        }
    }
}

在Java-6中,这会导致第一个挂起,第二个正常工作

显然,主要区别在于
getObj
可能会被两个不同的线程同时调用,甚至可能在另一个线程创建新对象时被调用

从这一点来看,你很可能没有处于那种情况。结论是,一个线程正在从
Map
(可能是
o=(MyClass)this.objCache.get(id);
)读取,而另一个线程正在通过调用
addObj
写入映射。这显然是一个让阅读崩溃和燃烧的方法


有关潜在落水洞的详细信息,请参阅。

要获得死锁,您需要尝试在不同线程中同时获得两个或多个锁。单靠一个锁不会死锁。您能否演示如何使用另一个锁以及如何以不同的顺序获得这些锁?注意:如果您以不安全的方式更新映射,您可能会进入无限循环。这一直存在。我会考虑让整个代码线程安全,如果需要并发访问,使用并发进程映射(java 5中添加)BTW,如果你有一个真正的死锁,并且都是使用内置的代码>同步< /Cord>关键字,那么线程转储实际上会检测到死锁并告诉你。如果你没有看到这一点,这是彼得理论的有力证据。这是有症状的。您正在一个接一个地执行一些不必要的散列查找,但试图通过双重检查锁定反模式来提高性能。查看
addObj
;如果(map.containsKey(key)){map.remove(key);}map.put(key,…),它会执行
if
,执行三次哈希查找,以完成单个
map.put(key,…)所做的操作仍然可以。或者,当键不存在时,
ejbCreate
中会发生什么,首先是一个未同步的
get
,然后是一个同步的
hasObj
containsKey
),然后是
addObj
(另一个
包含skey
,然后是
put
)总共进行四次查找…由于双重检查锁定,这段代码肯定会被破坏,但是,在一段只有一个锁的代码中不可能发生包含两个锁的死锁,但该代码显然不是您真正的代码,因为由于未处理/未声明的异常和缺少返回语句,它甚至无法编译,等等,你好,彼得。谢谢你的回复。这绝对是我们看到的死锁——org.jboss.util.deadlock.ApplicationDeadlockException。我们已经查看了线程,它确实是一个典型的死锁-线程1有a,想要B,线程2有B,想要a。@Paul你能给我们展示两个死锁的代码路径吗。@Paul:在你展示的代码中,只涉及一个锁,所以如果涉及两个锁的死锁,你的问题最多是不完整的。
public void ejbCreate1(String id) throws Exception {
    try {
        m_c = Singleton.getInstance().getObj(id);
    } catch (Exception e) {
        synchronized (Singleton.getInstance()) {
            //check again
            if (!Singleton.getInstance().hasObj(id)) {
                m_c = new MyClass(id);
                Singleton.getInstance().addObj(id, m_c);
            } else {
                m_c = Singleton.getInstance().getObj(id);
            }
        }
    }
}
public void ejbCreate2(String id) throws Exception {
    synchronized (Singleton.getInstance()) {
        try {
            m_c = Singleton.getInstance().getObj(id);
        } catch (Exception e) {
            //check again
            if (!Singleton.getInstance().hasObj(id)) {
                m_c = new MyClass(id);
                Singleton.getInstance().addObj(id, m_c);
            } else {
                m_c = Singleton.getInstance().getObj(id);
            }
        }
    }
}