Java实现单例模式

来源:互联网 发布:虚拟商品怎么淘宝介入 编辑:程序博客网 时间:2024/04/29 11:59

定义

单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理[1]。(来自wiki

构建方式

通常我们有两种方式来构建单例。

懒汉方式:指全局的单例实例在第一次被使用时构建。

饿汉方式:指全局的单例实例在类加载时构建。

构建代码

懒汉式(线程不安全)

public class Singleton {    private static Singleton INSTANCE = null;    private Singleton() {    }    public static Singleton getInstance() {        if (INSTANCE == null)            INSTANCE = new Singleton();        return INSTANCE;    }}

优点是容易理解,缺点是在多线程下可能会出现两个实例。

懒汉式(线程安全)

public class Singleton {    private static Singleton INSTANCE = null;    private Singleton() {    }    public static synchronized Singleton getInstance() {        if (INSTANCE == null)            INSTANCE = new Singleton();        return INSTANCE;    }}

优点是线程安全,缺点是效率低下,每次获取单例实例的时候都需要加锁,而加锁是一个非常耗时的操作,在没有必要的时候我们应该尽量避免。

懒汉式(DCL:双重检验锁)

public class Singleton {    private static Singleton INSTANCE = null;    private Singleton() {    }    public static synchronized Singleton getInstance() {        if (INSTANCE == null) {            synchronized (Singleton.class) {                if (INSTANCE == null) {                    INSTANCE = new Singleton();                }            }        }        return INSTANCE;    }}

老外对这种写法的评价是:Clever, but broken
DCL理论上无懈可击,实际上,现实世界并不是这样的。DCL无法保证在单处理器还是多处理器的机器上正常工作。DCL失败的问题不在于JVMs的bug,而是当前Java平台的内存模型。内存模型允许”out-of-order writes”的存在,并且它是DCL失败的罪魁祸首[2]

out-of-order writes

整个问题的关键就出在 INSTANCE = new Singleton();
这句代码可以分解为下面三步:

mem = allocate();           //为Singleton对象的创建分配空间INSTANCE = mem;             //此时的INSTANCE已经不为null,但是它还没有被初始化ctorSingleton(instance);     //调用Singleton的构造函数

当执行到第二步时,内存已经分配了,INSTANCE引用也已指向新分配内存,而构造函数却还未执行。在此时,如果另一个线程来check INSTANCE时,会发现该INSTANCE已经不为null了,于是直接越过同步块,返回INSTANCE所引用的构造了一半的实例。这就是导致DCL失败的原因[2]

网络上面有些文章说可以通过 volatile 关键字修饰INSTANCE使得DCL可以正常工作,但我看国外的文章说 volatile 也会产生其他的问题[2][3]

饿汉式(static final)

public class Singleton {    private final static Singleton INSTANCE = new Singleton();    private Singleton() {    }    public static Singleton getInstance() {        return INSTANCE;    }}

静态内部类

public class Singleton {    private static class SingletonHolder {        private static final Singleton INSTANCE = new Singleton();    }    private Singleton() {    }    public static Singleton getInstance() {        return SingletonHolder.INSTANCE;    }}

由于静态内部类在第一次使用的时候才会被加载,所以它属于懒汉式的,又因为这个实现过程没有加锁,所以在性能上也能达到很好的效果,JVM保证了线程安全。

饿汉式(枚举)

public enum EasySingleton{    INSTANCE;}

参考文献

1.https://zh.wikipedia.org/wiki/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F
2.http://www.ibm.com/developerworks/library/j-dcl/
3.http://www.javaworld.com/article/2074979/java-concurrency/double-checked-locking–clever–but-broken.html

0 0
原创粉丝点击