为什么这会陷入僵局?

时间:2016-01-06 11:52:58

标签: jpa javafx deadlock

您好我在JavaFX应用程序中遇到了死锁,我不确定为什么会发生这种情况...... 初始化我的应用程序时,我启动一个Thread来加载某个视图,该视图正在创建一个扩展我的DatabaseManager的对象。同时另一个Thread在另一个视图和另一个扩展DatabaseManager的对象上做同样的事情。

进入以下构造函数的第一个线程进入同步块但从未到达" System.out.println(" **** 3");"线。 发生这种情况后,我后来启动的线程进入构造函数,当然因为资源从未被释放而被阻止。 通过线程1。 任何想法,为什么这会导致僵局?我正在使用javafx.concurrent.Task和java.lang.Thread

public abstract class DatabaseManager {

protected static final AtomicReference<EntityManager> entityManager = new AtomicReference<>();

protected DatabaseManager() {
    if (entityManager.get() == null) {
                System.out.println("****1");
        synchronized (entityManager) {
            if (entityManager.get() == null) {
                System.out.println("****2");
                entityManager.set(Persistence.createEntityManagerFactory(
                        DatabaseConstants.hsqlPersistenceUnitName,
                        DatabaseConstants.getProperties()).createEntityManager());
                System.out.println("****3");
            }
        }
    }
}
...

2 个答案:

答案 0 :(得分:2)

AtomicReference(及其原始包装朋友)管理自己的原子性。因此,虽然我无法真正理解为什么这会导致死锁,但使用同步块来使用AtomicReference会首先击败AtomicReference的整个目的。

你可以这样做:

protected DatabaseManager() {
    entityManager.compareAndSet(null, 
         Persistence.createEntityManagerFactory(
             DatabaseConstants.hsqlPersistenceUnitName,
             DatabaseConstants.getProperties()).createEntityManager());
}

将与您尝试的效果完全相同(显然没有记录)。

懒惰地初始化静态字段的推荐方法是使用&#34; Lazy初始化持有者类习惯用法&#34;:

public abstract class DatabaseManager {

    protected static EntityManager getEntityManager() {
        return EntityManagerHolder.entityManager ;
    }

    private static class EntityManagerHolder {
        static final EntityManager entityManager = 
            Persistence.createEntityManagerFactory(
                DatabaseConstants.hsqlPersistenceUnitName,
                DatabaseConstants.getProperties()).createEntityManager() ;
        }
    }
}

这确保了延迟初始化,因为内部类DatabaseManager.EntityManagerHolder在第一次被引用之前不会被加载,这在第一次调用getEntityManager()之前不会发生。它保证原子,因为类初始化器是保证原子的。此外,由于仅在内部类初始化时强制执行原子性,因此在后续调用getEntityManager()时不会产生同步成本。 (相比之下,每次创建新AtomicReference时,AtomicReference.compareAndSet(...)的解决方案都会对DatabaseManager执行(可能是内部同步的)调用。)

请参阅Josh Bloch的 Effective Java ,第71项,以便进行更全面的讨论。

答案 1 :(得分:0)

我找到了解决我死锁问题的方法,但我不知道为什么会导致死锁... 我只有另一个尝试访问另一个数据库的线程。该应用程序与2个数据库进行交互。我的HSQL数据库上的所有执行都来自我的DatabaseManager,当一个线程试图在我的DatabaseManager中初始化EntityManager时,第三个线程只是调用

Persistence.createEntityManagerFactory(DBConstants.ORACLE_PERSISTENCE_UNIT).createEntityManager();

删除该行并使用DatabaseManager建立与第二个数据库的连接后,死锁消失了。 但我不明白为什么。我眼中唯一可能的解决方案是eclipselink本身陷入僵局......