单例模式(Singleton)

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

懒汉式式单例(Singleton)

      优点:没有加锁,执行效率会提高。 缺点:类加载时就初始化,浪费内存。

public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton (){}  //构造方法私有化
    public static Singleton getInstance() {
        return instance;
    }
}

不安全饿汉式(UnsafeLazySingleton 

       严格意义上它并不算单例模式。这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。

/**
 * @author buyi
 * 不安全的懒加载单例
 *
 * 不安全原因:
 * 1.假设A线程和B线程同时开始执行代码2,会new出两个对象
 * 1.假设A线程执行代码1的同时,B线程执行代码2。此时,线程A可能会看到instance引用的对象还没有完成初始化
 */
public class UnsafeLazySingleton {
    private static Object instance;
    public Object getSingleton() {
        if (null == instance) {          //代码1
            instance = new Object();     //代码2
        }
        return instance;
    }
}

安全饿汉式(SafeLazySingleton)

        具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。优点:第一次调用才初始化,避免内存浪费。缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。

/**
 * @author buyi
 * 安全的懒加载单例
 *
 * 待优化原因:
 * getSingleton()方法做了同步处理,synchronized将导致性能开销。如果getSingleton()方 法被多个线程频繁的调用,将会导致程序执行性能的下降。
 * 反之,如果getSingleton()方法不会被 多个线程频繁的调用,那么这个延迟初始化方案将能提供令人满意的性能。
 */
public class SafeLazySingleton {
    private static Object instance;
    public synchronized Object getSingleton() {
        if (null == instance) {          //代码1
            instance = new Object();     //代码2
        }
        return instance;
    }
}

基于双重校验锁(DoubleCheckLazySingleton) 

/**
 * @author buyi
 *
 * 万事大吉?
 *  在线程执行到##时,代码读取到instance不为null时,instance引用的对象有可能还没有完成初始化
 */
public class DoubleCheckLazySingleton {
    private static Object instance;
    public Object getSingleton() {
        if (null == instance) {                                        //第一次检查   ##
            synchronized (DoubleCheckLazySingleton.class) {            //加锁
                if (null == instance) {                                //第二次检查
                    instance = new Object();                           //实例化   
                }
            }
        }
        return instance;
    }
}
  问题根源:在于实例化new Object()时候,可以分解为三步骤 

      此时可能发生指令重排序2和3之间 

 

      那么线程A和线程B执行时序图可能为:


        A2和A3虽然重排序了,但Java内存模型的intra-thread semantics将确保A2一定会排在A4前面执行。因此,线程A的intra-thread semantics没有改变,但A2和A3的重排序,将导致线程B在B1处判断出instance不为空,线程B接下来将访问instance引用的对象。此时,线程B将会访问到一个还未初始化的对象。 

双重检查锁升级版DoubleCheckSafeLazySingleton 

      基于双重检查锁和volatile(防止指令重排序)的单例模式

/**
 * @author buyi
 *
 * 优化版基于双重检查锁和volatile的单例
 */
public class DoubleCheckSafeLazySingleton {
    private  DoubleCheckSafeLazySingleton(){}//构造方法私有化
private volatile static Object instance; public Object getSingleton() { 

if (null == instance) {
synchronized (DoubleCheckSafeLazySingleton.class){
if (null == instance) {
instance = new Object(); }
} }
return instance;
} }

静态内部类单例(Singleton

       SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。在延迟加载方面比懒汉式更好。

public class Singleton {  
    private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
    return SingletonHolder.INSTANCE;  
    }  
}
原文地址:https://www.cnblogs.com/dyg0826/p/11284239.html