单例模式(Singleton)小记

概念

引用维基百科对单例的说明:html

单例模式,也叫单子模式,是一种经常使用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在java

继续引用维基百科的实现思路:设计模式

实现单例模式的思路是:一个类能返回对象一个引用(永远是同一个)和一个得到该实例的方法(必须是静态方法,一般使用getInstance这个名称);当咱们调用这个方法时,若是类持有的引用不为空就返回这个引用,若是类保持的引用为空就建立该类的实例并将实例的引用赋予该类保持的引用;同时咱们还将该类的构造函数定义为私有方法,这样其余处的代码就没法经过调用该类的构造函数来实例化该类的对象,只有经过该类提供的静态方法来获得该类的惟一实例。安全

单例的思路就这么简单,不像其余的设计模式那样通常有几个类。它只有一个类。多线程

Java 中的单例实现

懒汉式与饿汉式

在讨论 Java 的单例模式时,猿们通常会提到二者实现方式:懒汉式饿汉式 。为何会有这两种叫法?看了实现代码后,相信您会突然大悟。并发

饿汉式,我是饿汉,不能等,建立(类)时就要给我吃的(实例化对象),否则我会饿死:函数

public class Singleton{
    //建立类时就实例化出单例。
    private final static Singleton INSTANCE = new Singleton();
    
    //使用私有的构造器来阻止外部(其余代码)实例化该对象
    private Singleton(){}

    //因为该类的构造方法是私有的,只能在该类内部实例化对象。所以必须提供一个静态的公有方法做为出口,提供单例。
    public static Singleton getInstance(){
        return INSTANCE;
    }
}

懒汉式, 我是懒汉,你叫我干活(给 INSTANCE new 一个单例)才干,懒是个人天性,这不能怪我:.net

public class Singleton{
    private static Singleton INSTANCE;

    private Singleton(){}

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

OK,单例模式就是这么简单,结束了。呵呵,真的结束了吗?图样图森破!线程

上面的两个实现确实是实现了汉式和懒汉式。并且它们在单线程下能很好地运行。但若是咱们把它们应用在多线程下呢?试一下就知道,饿汉式依然坚挺,潇洒应对。但懒汉式就瞬间爆炸了!没办法谁叫你懒,出来混老是要还的。即便是代码,懒也要付出代价~~开玩笑的^_^,爆炸的缘由确定不是懒的缘由啦,是线程同步的问题,形成了非线程安全的懒汉式。设计

既然上面的懒汉式在多线程下爆炸了,咱们就要去拯救它,总不能见死不救吧~~

多线程下懒汉式的自我拯救

知道懒汉式爆炸的缘由时线程同步的问题,咱们最简单的拯救方法就是直接进行同步加锁,建立线程安全的懒汉式

public class Singleton{
    private static Singleton INSTANCE;

    private Singleton(){}

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

经过简单的对 getInstance 方法进行加锁就能够把非线程安全的懒汉式改成线程安全的,简单吧!但,正如天下没免费的午饭同样,如此简单的拯救方法确定是要付出代价的。所以这种实现方法会下降效率。

好吧,效率低下,我改还不行。通过改改改后,咱们又得出了另一种线程安全懒汉式单例:双重校验锁(Double-Checked Locking)

public class Singleton{
    private static volatile Singleton INSTANCE;

    private Singleton(){}

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

该方法比以前的线程安全懒汉式效率高的缘由是,以前的实现方法每次获取单例是都要进行同步,每一次只能有一个线程进入 getInstance 方法获取实例;而该方法把同步块缩小了,并且只在 INSTANCE 为 null 时才会进行同步访问(也就是说只须要一次同步)。以后都不须要同步,能够并发地获取实例。

不少人都知道,JDK 1.5 以前利用这种方法实现是有问题的。但在 JDK 1.5 后,Java 的内存已经修改了,该方法在 1.5 后能正常运行,能够放心使用。

其余的实现方法

使用静态内部类实现

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

    private Singleton(){}

    public static final Singleton getInstance(){
        return SingletonHolder.INSTANCE;
    }
}

该方法利用了静态内部类的特性实现了相似与懒汉式的单例模式

(单元素的)枚举实现

public enum Singleton{
    INSTANCE;
}

枚举实现的单例已经简单到不能再简单了,能够直接使用 Singleton.INSTANCE 来获取单例。它虽然简单,但也是线程安全的实现方法,而且也是可序列化的。在 Effective Java 第二版里也推荐使用这种方法: 单元素的枚举类型已经成为实现 Singleton 的最佳方法

单例在 Java 中的应用

  • Java.awt.Toolkit with getDefaultToolkit()
  • Java.awt.Desktop with getDesktop()
  • java.lang.Runtime

java.lang.Runtime 使用的单例(饿汉式,不涉及到线程安全):

public class Runtime{
    private static Runtime currentRuntime = new Runtime();

    public static Runtime getRuntime(){
        return currentRuntime;
    }
    
    private Runtime(){}

    //... 其余方法
}

参考

维基百科
StackExchange
使用枚举实现单例 Effective Java 第三条建议

相关文章
相关标签/搜索