Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/360.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 关于双重检查单例_Java_Design Patterns - Fatal编程技术网

Java 关于双重检查单例

Java 关于双重检查单例,java,design-patterns,Java,Design Patterns,请详细解释一下关于双重检查单例。。!它的优点和缺点是什么。。!!我下课了,我怎么能成为一个双重检查的单身汉 private DataBaseDAO() { } public static synchronized DataBaseDAO getInstance() { if (dao == null) { dao = new DataBaseDAO(); } retu

请详细解释一下关于双重检查单例。。!它的优点和缺点是什么。。!!我下课了,我怎么能成为一个双重检查的单身汉

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

考虑下面的代码

public static Singleton getInstance()
{
  if (instance == null)
  {
    synchronized(Singleton.class) {  //1
      if (instance == null)          //2
        instance = new Singleton();  //3
    }
  }
  return instance;
}
双重检查锁定背后的理论是//2处的第二次检查使创建两个不同的单例对象变得不可能,如清单1所示

考虑以下事件顺序:

线程1进入getInstance()方法

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

线程1被线程2抢占

线程2进入getInstance()方法

线程2试图获取//1处的锁,因为实例仍然为null。但是,由于线程1持有锁,因此线程2在//1处阻塞

线程2被线程1抢占

线程1执行,因为instance在//2处仍然为null,所以创建一个单例对象并将其引用分配给instance

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

线程1被线程2抢占

线程2获取//1处的锁并检查实例是否为null

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

双重检查锁定背后的理论是完美的。不幸的是,现实完全不同。双重检查锁定的问题在于不能保证它能在单处理器或多处理器机器上工作。
双重检查锁定失败的问题不是由于JVM中的实现错误,而是由于当前的Java平台内存模型。内存模型允许所谓的“无序写入”,这也是该习惯用法失败的主要原因。

在代码实现中,您正在执行静态方法级同步。如果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
本身将失败

如中所述,一个好的解决方案是使用“Singleton as enum”模式:


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

请参阅这篇关于双重检查单例的文章:双重检查锁定习惯用法的主要缺点:除非使dao易失性,否则它是坏的。不要使用它。如果这个模式不起作用,你会推荐另一个习惯用法吗?@gotuskar..请解释第二行的用法..即..private static final Object lock=new Object()。。请解释它的功能-1这是一个你不应该做的好例子。这是一个损坏的设计。@user1582269检查:使用枚举。@assylias:肯定
enum
是最安全的实现之一。你能解释一下这个设计是怎么被打破的吗?@assylias:读起来很有趣。非常感谢您分享这些链接。这确实给了我一张好照片。
public enum DataBaseDAO() {
    INSTANCE;
}