`

单例模式与DCL

    博客分类:
  • java
 
阅读更多
在一些人的印象中,懒汉式单例可能是这样的:



public class Singleton {



    private static Singleton instance = null;

    private Singleton(){}




    public synchronized static Singleton getInstance() {



       if(instance == null) {

           instance = new Singleton();

       }



       return instance;

    }



}



这种写法,是安全的,不会产生多个实例。但是这种写法有个问题就是把synchronized关键字加在方法签名上,导致多线程访问的时候,锁是加在Singleton.class这个对象上的。每次调用getInstance都需要取得Singleton.class上的锁,然而该锁只是在开始构建Singleton 对象的时候才是必要的,后续的多线程访问,效率会降低,于是有了接下来的版本:



public class Singleton {

    private static Singleton instance = null;

    private Singleton(){}



    public static Singleton getInstance() {

       if(instance == null) {

           synchronized(Singleton.class) {

              if(instance == null) {

                  instance = new Singleton();

              }

           }

       }



       return instance;



    }

}



这种写法,看上去很美,甚至被当作懒汉式单例的正确版本广为流传。不幸的是,这个版本也是错误的。这和DCL有关。虽然我也听说过java中双重检查成例是失效的,但是没有弄得很清楚到底它是怎么失效的,依稀记得跟编译器优化代码有关系。今天看了一些资料,终于明白了大概:

上述代码,并不会产生两个Singleton实例,它的错误表现在虽然一个线程得到了Singleton的实例,但是里面的状态是不对的。如果要弄明白具体的意思,来看一下下面的例子:



# public class LazySingleton {

#     private int someField;

#     

#     private static LazySingleton instance;

#     

#     private LazySingleton() {

#         this.someField = new Random().nextInt(200)+1;         // (1)

#     }

#     

#     public static LazySingleton getInstance() {

#         if (instance == null) {                               // (2)

#             synchronized(LazySingleton.class) {               // (3)

#                 if (instance == null) {                       // (4)

#                     instance = new LazySingleton();           // (5)

#                 }

#             }

#         }

#         return instance;                                      // (6)

#     }

#     

#     public int getSomeField() {

#         return this.someField;                                // (7)

#     }

# }

这也是同样错误的懒汉式单例,和上面不同之处是多了一个someField,以及相应的赋值和取值代码。这个单例,在某些极端的情况下会发生这样的现象:线程X获得了单例,但是调用getSomeField()的时候,取到的值并不是一个大于1的随机数,而是0.这种情况仅仅可能发生在初次调用的时候,而且是多个线程几乎同时调用的时候。由于构造方法的调用在字节码中并不是一个指令,而是4到5个指令。比如最简单的new一个String,字节码如下:

String str = new String();



是分成下面几条指令执行的:

0:   new     #2; //class java/lang/String

1:   dup

2:   invokespecial   #3; //Method java/lang/String."<init>":()V

3:   astore_1



这样就会出现一种情况,实例已经创建了,但是构造方法还没被调用(<init>没被调用)。导致这个实例中的field都是默认的。



解决这个问题的办法是使用一个静态内部类来保存实例。如下面所示:



public class Something {

      

    private Something() {

        }



    private static class LazyHolder {

        public static final Something INSTANCE = new Something();

    }



    public static Something getInstance() {

        return LazyHolder.INSTANCE;



    }

}





或者,在JDK1.5之后,还可以采用另外一种办法,使用volatile关键字:



在JDK1.5及其后续版本中,扩充了volatile语义,系统将不允许对 写入一个volatile变 量的操作与其之前的任何读写操作 重新排序,也不允许将 读取一个volatile变量的操作与其之后的任何读写操作 重新排序。

在jdk1.5及其后的版本中,可以将instance 设置成volatile以让双重检查锁定生效,如下:

public class Singleton {

    private static volatile Singleton instance = null;

    private Singleton(){}

  

    public static Singleton getInstance() {

       if(instance == null) {

           synchronized(Singleton.class) {

              if(instance == null) {

                  instance = new Singleton();

              }

           }

       }

       return instance;

    }

}






看似小小的单例问题,竟然可以牵涉那么多的深层次问题。值得令人深思。





主题:用happen-before规则重新审视DCL http://www.iteye.com/topic/260515

双重检查锁定失败可能性——参照《The "Double-Checked Locking is Broken" Declaration》 http://freish.iteye.com/blog/1008304#bc2248177

分享到:
评论

相关推荐

    单例模式(饿汉模式、懒汉模式、DCL单例模式、枚举)

    本文将详细讨论四种常见的单例实现方式:饿汉模式、懒汉模式、双重检查锁定(DCL)单例模式以及枚举单例。 1. **饿汉模式**: 饿汉模式是在类加载时就完成了实例化,避免了线程同步问题。这种方式简单且安全,但...

    设计模式之单例模式(结合工厂模式)

    将单例模式与工厂模式结合,可以创建一个单例工厂,这个工厂类负责生成单例对象。这样做有两个主要好处:一是隐藏了单例的实现细节,使得代码更加整洁,降低了耦合度;二是可以通过工厂方法扩展新的实现,如果将来...

    C#单例模式详解 C#单例模式详解C#单例模式详解

    单例模式是软件设计模式中的一种,它保证一个类只有一个实例,并提供一个全局访问点。在C#中,单例模式常用于管理共享资源或控制类的实例化过程,以提高性能、节约系统资源,特别是在整个应用程序生命周期内只需要一...

    单例模式详解~~单例模式详解~~

    单例模式是一种设计模式,它的主要目标是确保一个类只有一个实例,并提供一个全局访问点。在软件工程中,单例模式常用于控制资源的共享,比如数据库连接池、线程池或者日志系统等,这些资源通常需要全局唯一且高效地...

    设计模式——单例模式

    **设计模式——单例模式** 在软件工程中,设计模式是一种在特定场景下解决常见问题的标准方案,可以被复用并提升代码质量。单例模式是设计模式中的一种,它保证一个类只有一个实例,并提供一个全局访问点。这种模式...

    7种单例模式

    单例模式是软件设计模式中的一种经典模式,其主要目的是确保一个类只有一个实例,并提供一个全局访问点。这种模式在很多场景下非常有用,比如控制共享资源、管理配置对象等。下面将详细介绍七种常见的单例模式实现...

    单例模式和工厂模式代码

    在Java中,实现单例模式有多种方法,包括懒汉式(线程不安全)、饿汉式(静态常量)、双检锁(DCL)和枚举单例。其中,双检锁和枚举单例是线程安全的,推荐在多线程环境下使用。 ```java // 双检锁/双重校验锁(DCL...

    使用单例模式创建学生管理系统(饿汉式、懒汉式)

    单例模式是软件设计模式中的一种,它的主要目的是确保一个类只有一个实例,并提供一个全局访问点。在Java或类似的面向对象编程语言中,单例模式常用于管理共享资源,如数据库连接池、线程池或者配置文件等。在这个...

    几种单例模式demo

    单例模式是软件设计模式中的一种,它的主要目的是确保一个类只有一个实例,并提供一个全局访问点。这种模式在很多场景下都非常有用,比如控制资源的唯一性、管理共享配置或者创建昂贵的对象时避免频繁创建销毁。 ...

    Java 单例模式 工具类

    Java中的单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供全局访问点。在Java编程中,单例模式常用于控制资源的访问,比如数据库连接池、线程池或者日志对象等。本篇文章将深入探讨如何在Java中...

    设计模式之单例模式源码demo

    3. **双重检查锁定(DCL,Double Check Locking)单例模式**:结合了饿汉模式和懒汉模式的优点,既延迟了初始化,又保证了线程安全。在多线程环境下,只有在`instance`为`null`时才会进入同步块,避免了不必要的同步...

    设计模式——单例模式(懒汉模式)

    下面是一个简单的DCL实现的懒汉式单例模式代码示例: ```java public class Singleton { private volatile static Singleton instance; // 使用volatile关键字防止指令重排序 private Singleton() {} public ...

    23钟设计模式之单例模式

    单例模式有多种实现方式,常见的包括懒汉式、饿汉式以及双重检查锁定(DCL)等。 1. 懒汉式:懒汉式是在第一次使用时才创建单例对象,以延迟加载提高效率。但是,如果在多线程环境下,没有正确处理同步问题,可能...

    单例模式的要点及模式

    ### 三、单例模式的应用场景与优缺点 **应用场景**: - 全局日志对象 - 数据库连接池 - 缓存服务 - 线程池 - 多线程环境下的线程同步控制 **优点**: - 节省内存空间,减少系统资源消耗。 - 避免对资源的多重占用...

    设计模式单例模式

    单例模式是软件设计模式中的一种基础且广泛应用的模式,它的主要目的是确保一个类只有一个实例,并提供一个全局访问点。这种模式在系统中需要频繁创建和销毁对象,且对象创建成本较高,或者需要共享资源的情况下非常...

    Java单例模式应用研究.pdf

    #### 五、双重检查锁(DCL)单例模式 双重检查锁单例模式(Doubly Checked Locking Singleton)是懒汉式单例模式的一种改进版,既实现了延迟加载,又解决了多线程安全问题,同时也减少了同步的开销。具体实现如下: ...

    大话设计模式--Singleton(单例模式)

    单例模式是软件设计模式中的一种经典模式,它在许多场景下被广泛使用,尤其是在需要全局唯一实例的情况下。本文将深入探讨单例模式的概念、作用、实现方式以及其在实际编程中的应用。 单例模式的核心思想是确保一个...

    单例模式各种实现方式

    单例模式是软件设计模式中的一种基础且广泛应用的模式,它的主要目的是确保一个类只有一个实例,并提供一个全局访问点。在Java中,实现单例模式有多种方法,每种都有其特定的优缺点和适用场景。以下是几种常见的单例...

    设计模式——策略模式 & 单例模式

    策略模式和单例模式是软件设计中两种非常重要的设计模式,它们在实际开发中有着广泛的应用。在这篇文章中,我们将深入探讨这两种模式的核心概念、实现方式以及如何在实际项目中运用。 策略模式是一种行为设计模式,...

    Java单例模式深入理解

    Java单例模式是一种设计模式,它允许在程序中创建唯一一个类实例,通常用于管理共享资源,例如数据库连接、线程池或者配置对象等。单例模式的核心在于限制类的构造函数,确保类只能被初始化一次,从而实现全局唯一的...

Global site tag (gtag.js) - Google Analytics