【问题标题】:Synchronized and locks in singleton factory在单例工厂中同步和锁定
【发布时间】:2016-06-12 22:33:47
【问题描述】:

我有一个单例工厂(编辑:重命名为“加载器”以避免与工厂模式混淆)创建对象(在我的示例 DAO 中)或在已创建的情况下返回它们:

public class DAOLoader {

    private static final DAOLoader INSTANCE = new DAOLoader();

    private UserDAO userDAO;
    private MessageDAO messageDAO;

    private final Object lockUserDAO = new Object();
    private final Object lockMessageDAO = new Object();

    private DAOLoader() {}

    public static DAOLoader getInstance() {
        return INSTANCE;
    }

    public UserDAO getUserDAO() {
        if (userDAO == null) {
            synchronized(lockUserDAO) {
                if (userDAO == null) userDAO = new UserDAO();
            }
        }
        return userDAO;
    }

    public MessageDAO getMessageDAO() {
        if (messageDAO == null) {
            synchronized(lockMessageDAO) {
                if (messageDAO == null) messageDAO = new MessageDAO();
            }
        }
        return messageDAO;
    }
}

首先,你们觉得这段代码有什么问题吗?
在此示例中,是否需要为每种方法使用不同的锁,还是应该只使用 1 个全局锁?唯一的全局锁会发生死锁吗?如果不是,唯一的缺点是,如果某个线程使用锁来创建 DAO,而另一个线程想创建另一个 DAO,它必须等待锁被释放?

谢谢。

【问题讨论】:

  • 唯一的缺点?远不止于此。像全局可见的对象不会缩放。谷歌有一个单身猎人来识别和删除他们:reddit.com/comments/ikf9z
  • 我认为那里甚至没有工厂设计。检查这个tutorialspoint.com/design_pattern/factory_pattern.htm
  • @duffymo “全局可见的对象不会缩放”你是什么意思?
  • @NathanHughes 这是一个我不能使用 Spring 或任何 DI 框架的项目,我只想创建 1 次 DAO 并重用它们。
  • 让我为您简化:不要创建单例或所有这些锁定代码。汇集您的连接并在尽可能窄的范围内使用它们。学习 Spring——他们编写的 JDBC 框架比你想象的要好。

标签: java multithreading locking singleton synchronized


【解决方案1】:

您的示例似乎有点混乱,因为您阻止了 DaoLoader 的构造函数可见,但您并没有阻止 Dao 构造函数可见。还拥有一个加载器类可以变成一个垃圾场,它鼓励按层而不是按功能组织。

您可以考虑使用Initialization-on-Demand holder idiom:

public class UserDao {
    private UserDao() {}

    String findById(Long id) {
        return "foo";
    }

    private static class LazyUserDaoHolder {
        static final UserDao USER_DAO_INSTANCE = new UserDao();
    }

    public static UserDao getInstance() {
        return LazyUserDaoHolder.USER_DAO_INSTANCE;
    }
}

在调用访问它的方法之前不会初始化持有者静态类,因为它是在第一次访问时初始化的(并且类初始化是串行的)不需要同步。

【讨论】:

  • @Ravindra:这就是这里的 getInstance 方法。我冒昧地简化了发布的示例并摆脱了 DaoLoader。
  • 我觉得比双锁单例要好。顺便说一句,USER_DAO_INSTANCE 是否需要易变?
  • @Ravindra:不,它不需要是易变的。因为它是最终的,所以在初始化类时会安全地发布。
  • 知道了。我之前已经迷失在懒惰单例的双重锁定蓝调中了 :)
  • @NathanHughes 正如我在问题下的 cmets 中所说,急切的初始化是故意的。 DAO 构造函数是可见的,因为它们可能会或可能不会通过加载程序使用,我也将参数传递给它们(如数据源)。无论如何,我的问题更多是关于唯一/相同的锁和同步;-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-12
  • 2011-05-25
  • 1970-01-01
  • 1970-01-01
  • 2012-06-25
  • 1970-01-01
相关资源
最近更新 更多