设计模式 - 单例模式

114 阅读2分钟

概述


单例模式属于创建型模式,保持对象实例在一个jvm内唯一,节省有限的资源消耗。

推荐单例的创建方式:

  1. 线程安全
  2. 懒加载
  3. 调用效率高(不用加锁)

思路


  1. 私有构造函数
  2. 私有静态实例
  3. 对外暴露公共的静态方法获取静态实例

类图


未命名文件.png

创建方式(5种)


  • 饿汉式(非懒加载)
/**
 * 饿汉式单例模式(在类加载的时候对实例进行初始化,导致没有调用 getSingleton() 时也占用了内存)
 * 特点:线程安全、非懒加载、效率高
 */
public class SingletonHungry {
    private static final SingletonHungry singleton = new SingletonHungry();
    private SingletonHungry() {}
    public static SingletonHungry getSingleton() {
        return singleton;
    }
}
  • 懒汉式(加锁调用效率低)
/**
 * 懒汉式单例模式
 * 特点:线程安全、懒加载、效率低
 */
public class SingletonLazy {
    private static SingletonLazy singleton;
    private SingletonLazy() {}

    // 线程安全,但每次调用需要加锁,效率低
    public static synchronized SingletonLazy getSingleton() {
        if (null == singleton) {
            singleton = new SingletonLazy();
        }
        return singleton;
    }
}
  • 双重检查加锁(推荐)
/**
 * 双重检查加锁
 * 特点:线程安全、懒加载、效率高
 */
public class SingletonDCL {
    // 关键字 volatile 防止指令重排序,从而导致 singleton 不为空但没有完成对象的初始化操作
    private static volatile SingletonDCL singleton;
    private SingletonDCL() {}

    public static SingletonDCL getSingleton() {
        if (null == singleton) {
            synchronized (SingletonDCL.class) {
                if (null == singleton) {
                    // 多线程环境下此处可能出现指令重排序,导致最外层的if判断失效,但此时 singleton 未完成初始化
                    singleton = new SingletonDCL();
                }
            }
        }
        return singleton;
    }
}
  • 静态内部类(推荐)
/**
 * 静态内部类(推荐)
 * 特点:线程安全、懒加载、效率高
 * 《Java Concurrency in Practice》作者Brian Goetz推荐使用的方式
 */
public class SingletonStaticInner {
    private static class SingletonStaticInnerHolder {
        private static final SingletonStaticInner singleton = new SingletonStaticInner();
    }

    private SingletonStaticInner() {}

    public static SingletonStaticInner getSingleton() {
        return SingletonStaticInnerHolder.singleton;
    }
}
  • 枚举(推荐)
/**
 * 枚举类(推荐,由于枚举的特性,通过反射和序列化/反序列化无法破解单例)
 * 特点:线程安全、非懒加载、效率高
 * 《Effective Java》作者Joshua Bloch推荐使用的方式
 */
public enum SingletonEnum {
    INSTANCE;
}

总结


Spring中bean默认的创建方式就是单例的,工作中大多使用静态内部类的方式创建单例。