关于双重检查单身人士

时间:2012-08-08 16:31:35

标签: java design-patterns

请详细告诉我有关双重检查单身人士的信息......!有什么优点和缺点.. !!我有这个下面的课程..我怎么能把它作为一个双重检查单身..

 private DataBaseDAO() { }
        public static synchronized DataBaseDAO getInstance() {
            if (dao == null) {
                dao = new DataBaseDAO();
                }
            return dao;
            }
        }
    }

3 个答案:

答案 0 :(得分:3)

考虑以下代码

public static Singleton getInstance()
{
  if (instance == null)
  {
    synchronized(Singleton.class) {  //1
      if (instance == null)          //2
        instance = new Singleton();  //3
    }
  }
  return instance;
}

双重检查锁定背后的理论是,在// 2处的第二次检查使得无法创建两个不同的Singleton对象,如清单

中所示。

考虑以下事件序列:

线程1进入getInstance()方法。

线程1在// 1处进入同步块,因为实例为空。

线程1被线程2抢占。

线程2进入getInstance()方法。

线程2尝试在// 1处获取锁定,因为实例仍为空。但是,因为线程1持有锁,所以线程2在// 1处阻塞。

线程2被线程1抢占。

线程1执行,因为实例在// 2处仍然为空,创建一个Singleton对象并将其引用分配给实例。

线程1退出synchronized块并从getInstance()方法返回实例。

线程1被线程2抢占。

线程2在// 1获取锁定并检查实例是否为空。

因为实例是非null,所以不会创建第二个Singleton对象,并返回由线程1创建的对象。

双重检查锁定背后的理论是完美的。不幸的是,现实完全不同。双重检查锁定的问题在于无法保证它可以在单处理器或多处理器机器上运行。 双重检查锁定失败的问题不是由于JVM中的实现错误,而是由于当前的Java平台内存模型。内存模型允许所谓的"无序写入"并且是这个成语失败的主要原因。

答案 1 :(得分:3)

"Effective Java Second Edition"所述,一个好的解决方案是使用“Singleton-as-enum”模式:

public enum DataBaseDAO() {
    INSTANCE;
}

并通过DataBaseDAO.INSTANCE访问它。在所有情况下,这都保证只有一个DataBaseDAO实例。

答案 2 :(得分:0)

在您的代码实现中,您正在进行静态方法级别同步。如果DatabaseDAO类中还有其他静态方法,那么即使这些方法可以使用这些方法独立工作,它们也无法实现。您可以通过执行块级同步来避免这种情况。在多线程应用程序中,这是实现单例对象的最安全的方法之一 -

private DataBaseDAO {
    private static final Object lock = new Object();
    private static DataBaseDAO dao = null;
    private DataBaseDAO() { }
    public static DataBaseDAO getInstance() {
        if (dao == null) {
            synchronize(lock) {
               if (dao == null) {
                   dao = new DataBaseDAO();
                }
            }
        }
        return dao;

    }
}

作为对象锁定,您还可以使用DataBaseDAO.class

说明:多个线程同时访问getInstance()方法。在交错场景中,2个或更多线程可以通过第一个if检查,但只有一个线程将获取lock对象的锁定。获取锁的线程将能够创建实例。其他线程,即使已经通过第一次if检查,如果第一个线程已经获得它(并且第一个线程尚未释放它),将无法获得锁定。

现在,假设第一个线程释放了锁,这意味着它还实例化了dao对象。当调度其他线程时,每个线程将获取锁,但第二个if检查将失败并立即释放锁并获取已经实例化的dao对象。

创建对象后,所有尝试访问getInstance()方法的线程都不会进行任何同步,因为第一个if本身将失败。