单例模式
设计模式对于开发者来说一般都不陌生,日常开发过程中肯定都用过其中几种,但是大部分人对设计模式了解的都不深刻,当然包括我自己,以前需要用到的时候去网上搜一下代码复制过来就可以用了,原理也能说一说,但是也只是一知半解,说来惭愧,工作两年,现在才开始有写博客的想法,所以就从设计模式开始。希望养成始终学习并记录下来的习惯。共勉。
单例模式属于比较常见的一种设计模式。它的目的就是确保某个类只有一个实例,因为有些对象的创建比较消耗资源并且在系统中存在且只应该有一个,比如阿里的oss,在使用的过程中我们需要实例化client对象,这时候可以使用单例模式。
单例模式的实现有多种,这里会讲述各自实现的优缺点。
1. 普通单例模式
/** * created by xushilong on 2018/10/8. */ public class SimpleSingleton { private static SimpleSingleton simpleSingleton; private SimpleSingleton() { } public static SimpleSingleton getInstance() { if (simpleSingleton == null) { simpleSingleton=new SimpleSingleton(); } return simpleSingleton; } }
这种实现方式代码简单明了,但是有一点缺憾就是在多线程的时候会创建多个实例,一个线程执行到判断simpleSingleton为空,实例化但未完成的时候,另外一个线程也执行到这里就会创建多个实例,这显然不符合单例模式的定义。
2. 饿汉单例模式
/** * created by xushilong on 2018/10/8. */ public class HungryManSingleton { //编译的时候就初始化对象 private static final HungryManSingleton hungryManSingleton=new HungryManSingleton(); private HungryManSingleton() {} public static HungryManSingleton getInstance() { return hungryManSingleton; } }
这种实现方式因为在编译的时候就实例化了对象,所以解决了多线程下创建多个实例的问题,但是因为在类加载的时候就实例化了对象,如果存在大量的这种类的时候,会占用较多内存,影响性能,所以这种实现方法也不是特别好。
3. 懒汉单例模式
/** * created by xushilong on 2018/10/8. */ public class LazyManSingleton { private static LazyManSingleton lazyManSingleton; private LazyManSingleton() {} public static synchronized LazyManSingleton getInstance() { if (lazyManSingleton == null) { lazyManSingleton=new LazyManSingleton(); } return lazyManSingleton; } }
这种实现方法与普通单例模式的唯一区别就是实例化方法前面多加了synchronized关键字,让该方法变成了一个同步方法,这样的确解决了编译的时候就初始化可能会损耗性能的问题,也解决了多线程会创建多个实例的问题,但是因为这个方法变成了一个同步方法,每一次调用都会进行同步(锁有关操作),也会一定程度上影响性能。所以这种方式也不推荐使用。
4. DCL(双重检查)单例模式
/** * created by xushilong on 2018/10/8. */ public class DoubleCheckSingleton { private static DoubleCheckSingleton doubleCheckSingleton; private DoubleCheckSingleton() {} public static DoubleCheckSingleton getInstance() { if (doubleCheckSingleton == null) { //第一次判空,避免了每次实例化都要进行synchronized同步操作,节约性能 synchronized (DoubleCheckSingleton.class) { if (doubleCheckSingleton == null) { //第二次判空,避免了多线程下创建多个实例的情况,所以这里两次判空都是有必要的 doubleCheckSingleton=new DoubleCheckSingleton(); } } } return doubleCheckSingleton; } }
关于为何要两次判空,代码里面注释已经写的很清楚了,这种实现方法避免了性能的消耗,多线程下也不会创建多个实例,推荐使用。
5. 静态内部类单例模式
/** * created by xushilong on 2018/10/8. */ public class InnerClassSingleton { private InnerClassSingleton() {} public static InnerClassSingleton getInstance() { return InnerClassBuilder.innerClassSingleton; } private static class InnerClassBuilder { private static final InnerClassSingleton innerClassSingleton=new InnerClassSingleton(); } }
因为外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化实例,故而不占内存。调用getInstance()方法会导致虚拟机加载内部类,这种方法不仅能确保线程安全,也能保证单例的唯一性,所以这种实现方法也是值得推荐的。剩下还有枚举单例和容器单例模式,因为个人觉得挺不方便的,这里就不做介绍了。
总结起来,单例模式的实现方法推荐使用DCL单例模式和静态内部类单例模式。这里需要注意的是,关于文中多次讨论的创建多个实例的情况,这里是忽略反射机制的,因为通过反射可以使上面所有的实现方法都能创建多个实例,因此请忽略掉反射机制。还有一个就是序列化。通过反序列化生成的实例也会和getInstance()方法生成的实例不是同一个实例。如果需要考虑这方面因素,可以在上面的代码的类中添加readResole()方法。
private Object readResolve() { return lazyManSingleton; }

浙公网安备 33010602011771号