1

单例模式(Singleton Pattern)

有时候又称“单件模式”或者“单态模式”,是Java中比较常见的创建型设计模式,他的核心是确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。比较经典的例子就是Windows系统中的“回收站”。

如何确保一个类在任何情况下都绝对只有一个实例?是单例模式设计的主要实现方向。下面介绍下单例模式的主要实现方法。

饿汉式实现

在单例类首次加载时就创建实例:

/**
 * 饿汉式单例
 *
 * 不管有没有调用,在类加载时直接实例化单例
 *
 * 缺点,类加载时就创建实例,浪费空间
 */
public class HungrySingleton {
    /**
     * 类加载时创建初始化
     */
    private static final HungrySingleton INSTANCE = new HungrySingleton();
    
       /**
     * 私有化构造方法
     */
    private HungrySingleton(){}
    
    /**
     * 提供全局访问点
     * @return 单例对象
     */
    public static HungrySingleton getInstance() {
        return INSTANCE;
    }
}

优化版的饿汉式单例模式:

/**
 * 饿汉式静态单例, 对饿汉式单例的进一步优化
 *
 * 缺点,类加载时就创建实例,浪费空间
 */
public class HungryStaticSingleton {

    /**
     * 类加载时创建初始化
     */
    private static final HungryStaticSingleton INSTANCE;

    /**
     * 在静态代码块中初始化
     */
    static {
        INSTANCE  = new HungryStaticSingleton();
    }

    /**
     * 私有化构造方法
     */
    private HungryStaticSingleton(){}

    /**
     * 提供全局访问点
     * @return 单例对象
     */
    public static HungryStaticSingleton getInstance() {
        return INSTANCE;
    }
}

序列化单例

/**
 * 饿汉式 防止序列化破坏单例
 * <p>
 *
 * 懒汉式序列化破坏单例模式
 *
 * 缺点,类加载时就创建实例,浪费空间
 */
public class SerializableSingleton implements Serializable {

    /**
     * 类加载时创建初始化
     */
    private static final SerializableSingleton INSTANCE = new SerializableSingleton();

    /**
     * 私有化构造方法
     */
    private SerializableSingleton() {
    }

    /**
     * 提供全局访问点
     *
     * @return 单例对象
     */
    public static SerializableSingleton getInstance() {
        return INSTANCE;
    }


    /**
     * 防止破坏单例模式,需新增此方法
     * <p>
     * 新增readResolve,是为了覆盖反序列化出来的对象
     * 整个单例过程中创建了两次,发生在JVM层面,相对来说比较安全
     * 之前反序列化的对象会被GC回收掉
     *
     * @return 单例对象
     */
    private Object readResolve() {
        return INSTANCE;
    }
}

懒汉式实现

/**
 * 懒汉式简单单例
 * <p>
 * 在调用时实例化单例,
 * <p>
 * 缺点:由于加锁造成的性能问题
 */
public class LazySimpleSingleton {

    /**
     * 类加载时不初始化实例
     */
    private static LazySimpleSingleton INSTANCE;

    /**
     * 私有化构造方法
     */
    private LazySimpleSingleton() {
    }

    /**
     * 等到调用时初始化单例实例
     * <p>
     * 提供全局访问点
     * <p>
     * 如果没有synchronized关键字,容易出现线程安全问题,因此需要添加synchronized进行同步,
     * 在方法添加synchronized关键字,可能造成整个类都会因为方法上的synchronized关键字被锁,
     * 因此一般都会在方法内部添加synchronized关键字
     *
     * @return 单例对象
     */
    public static synchronized LazySimpleSingleton getInstance() {
        //这里需要办法保证只有一个实例
        if (INSTANCE == null)
            INSTANCE = new LazySimpleSingleton();
        return INSTANCE;
    }
}

双重检查锁单例

/**
 * 懒汉式 双重检查锁单例
 * <p>
 * 为了保证单例的线程安全,使用双重加锁的方式
 * 问题:加锁所造成的性能问题
 */
public class LazyDoubleCheckSingleton {

    /**
     * 类加载时不初始化实例
     * <p>
     * volatile 可以防止指令重排序问题
     */
    private volatile static LazyDoubleCheckSingleton INSTANCE;

    /**
     * 私有化构造方法
     */
    private LazyDoubleCheckSingleton() {
    }

    /**
     * 等到调用时初始化单例实例
     * <p>
     * 提供全局访问点
     * <p>
     * 如果没有 synchronized关键字,容易出现线程安全问题,因此需要添加synchronized进行同步
     *
     * @return 单例对象
     */
    public static LazyDoubleCheckSingleton getInstance() {
        //这里需保证只有一个实例
        if (INSTANCE == null)
            synchronized (LazyDoubleCheckSingleton.class) {
                if (INSTANCE == null)//防止重复创建
                    INSTANCE = new LazyDoubleCheckSingleton();
                /*
                 * CPU执行时会转换成JVM指令执行
                 *
                 * 1.分配内存给这个对象
                 * 2.初始化对象
                 * 3.将初始化后的对象和内存地址建立关联,赋值
                 * 4.用户初次访问
                 *
                 */
                //指令重排序问题, 所以会在单例对象添加volatile修饰
            }
        return INSTANCE;
    }
}

懒汉式+饿汉式性能最优的单例写法

/**
 * 懒汉式 内部类单例
 *
 * 内部使用内部类的饿汉式单例,来避免线程安全问题,
 * 外部使用懒汉式单例,来减少内存开销
 *
 * 性能最优的单例写法
 *
 * 存在问题:反射调用的时候会破坏此单例模式
 */
public class LazyInnerClassSingleton {

    /**
     * 私有化构造方法
     */
    private LazyInnerClassSingleton() {
        throw new RuntimeException("Not support!");//防止反射破坏单例
    }

    /**
     * 懒汉式单例
     *
     * <p>
     * 提供全局访问点
     * <p>
     * LazyHolder等到此方法调用时,才执行,而内部类又使用饿汉式,完美避免了线程安全问题
     *
     * @return 单例对象
     */
    public static LazyInnerClassSingleton getInstance() {
        return LazyHolder.INSTANCE;
    }

    /**
     * 内部类实例单例模式
     */
    private static class LazyHolder {
        private static final LazyInnerClassSingleton INSTANCE =
                new LazyInnerClassSingleton();
    }
}

注册式实现

/**
 * 容器式单例
 *
 * 容器式单例都属于注册式单例模式,其核心思想是:
 * 在使用时,先去容器中查找,如果找到了,就将查出来的对象返回
 * 否则,实例化,然后转载到容器中,最后将实例化的对象返回
 */
public class ContainerSingleton {
    /**
     * 单例容器
     */
    private static final Map<String, Object> ioc = new ConcurrentHashMap<>();

    /**
     * 私有化构造函数
     */
    private ContainerSingleton() {
    }

    /**
     * 容器式单例模式
     *
     *  存在线程安全问题,因此需要使用synchronized关键字来加锁
     *
     * @param key 获取单例的key
     * @return 单例对象
     */
    public static Object getBean(String key) {
        if (ioc.containsKey(key)) {//如果有就取出返回
            return ioc.get(key);
        }

        //如果没有,新建-装载-返回
        try {
            Object instance = Class.forName(key).newInstance();
            ioc.put(key, instance);
            return instance;
        } catch (Exception e) {
            e.printStackTrace();
        }
        //装载异常, 返回空
        return null;
    }
}

最典型的就是枚举式单例

/**
 * 枚举单例模式
 * 属于装载类单例模式
 * <p>
 * 在调用时,先查询容器中是否有此对象的实例,有就取出直接返回,否则新建一个实例并且将其装载到容器中
 * 体现在Enum.valueOf((Class)cl, name);这个方法上
 */
 public enum EnumSingleton {
    INSTANCE;

    /**
     * 用来扩展的对象
     */
    private Object object;

    public Object getObject() {
        return object;
    }

    public void setObject(Object object) {
        this.object = object;
    }

    /**
     * 提供全局访问点
     *
     * @return 单例对象
     */
    public static EnumSingleton getInstance() {
        return INSTANCE;
    }
}

线程池单例

/**
 * 伪线程安全
 * 属于注册式单例模式
 * 即装载式单例
 * <p>
 * 案例: ThreadLocal来实现多数据源动态切换
 */
 public class ThreadLocalSingleton {

    /**
     * ThreadLocal实现单例模式
     */
    private static final ThreadLocal<ThreadLocalSingleton> local = new ThreadLocal<ThreadLocalSingleton>() {
        @Override
        protected ThreadLocalSingleton initialValue() {
            return new ThreadLocalSingleton();
        }
    };

    /**
     * 私有化构造方法
     */
    private ThreadLocalSingleton() {
    }


    /**
     * 提供全局访问点
     *
     * @return 单例对象
     */
    public static ThreadLocalSingleton getInstance() {
        return local.get();
    }
}

简单总结

  • 私有化构造器
  • 保证线程安全
  • 延迟加载
  • 防止序列化和反序列化破话单例
  • 防御反射攻击单例

流逝的傷
17 声望0 粉丝

本人很懒!