运做方式是这样的: 若是建立了一个对象,同时过一下子后决定再建立一个新对象,此时会得到以前已建立的对象, 而不是一个新对象。 java
注意, 普通构造函数没法实现上述行为,由于构造函数的设计决定了它必须老是返回一个新对象。git
和全局变量同样, 单例模式也容许在程序的任何地方访问特定对象。同时能够保护该实例不被其余代码覆盖。程序员
单例模式是一种建立型设计模式,可以保证一个类只有一个实例,并提供一个访问该实例的全局节点。github
注意:能够随时调整限制并设定生成单例实例的数量,只需修改 获取实例
方法,即 getInstance
中的代码便可实现。算法
若是能将对象的全部共享状态简化为一个享元对象,那么享元模式就和单例模式相似。但这个两个设计模式有两个根本性的不一样。数据库
静态类使用设计模式
/** * 静态类单例实现 */ public class StaticSingleton { public static Map<String,String> hashMap = new ConcurrentHashMap<String, String>(); }
单例模式的实现方式比较多,主要是实现上是否支持懒汉模式、是否线程安全。安全
接下来经过不一样的单例模式实现方式来解析单例模式。多线程
/** * 懒汉单例模式(线程不安全) */ public class SlobThreadUnsafeSingleton { private static SlobThreadUnsafeSingleton instance; /** * 单例的构造函数必须永远是私有类型,以防止使用`new`运算符直接调用构造方法 */ private SlobThreadUnsafeSingleton() { } public static SlobThreadUnsafeSingleton getInstance() { if (ObjectUtils.isEmpty(instance)) { instance = new SlobThreadUnsafeSingleton(); } return instance; } }
这种懒汉单例模式知足了懒加载,但当多个访问者同时获取对象实例时(多进程),会致使多个实例并存,没有达到单例模式的需求。架构
/** * 懒汉单例模式(线程安全) */ public class SlobThreadSafeSingleton { private static SlobThreadSafeSingleton instance; /** * 单例的构造函数必须永远是私有类型,以防止使用`new`运算符直接调用构造方法 */ private SlobThreadSafeSingleton() { } public static synchronized SlobThreadSafeSingleton getInstance() { if (ObjectUtils.isEmpty(instance)) { instance = new SlobThreadSafeSingleton(); } return instance; } }
这种懒汉单例模式是线程安全的,但因为锁在方法上,全部的访问都须要锁占用,会致使资源的浪费。非特殊状况,不建议使用此种方式来实现单例模式。
/** * 饿汉单例模式(线程安全) */ public class EagerSingleton { private static EagerSingleton instance = new EagerSingleton(); /** * 单例的构造函数必须永远是私有类型,以防止使用`new`运算符直接调用构造方法 */ private EagerSingleton() { } private static EagerSingleton getInstance() { return instance; } }
饿汉单例模式并非懒加载,简单来讲就是不管是否用到该类都会在程序启动之初建立。这种方式会致使的问题相似一打开某个软件,手机直接卡死(开启了过多无用功能,致使内存不足)。
/** * 双重校验锁单例模式(线程安全) */ public class DoubleCheckingLockingSingleton { private static volatile DoubleCheckingLockingSingleton instance; /** * 单例的构造函数必须永远是私有类型,以防止使用`new`运算符直接调用构造方法 */ private DoubleCheckingLockingSingleton() { } public static DoubleCheckingLockingSingleton getInstance() { if (ObjectUtils.isNotEmpty(instance)) { return instance; } synchronized (DoubleCheckingLockingSingleton.class) { if (ObjectUtils.isEmpty(instance)) { instance = new DoubleCheckingLockingSingleton(); } } return instance; } }
双重校验锁方式实现的单例模式是方法级锁的优化,减小了部分获取实例的耗时,同时也知足了懒加载。
/** * 静态内部类单例模式(线程安全) */ public class StaticInnerSingleton { private static class SingletonHolder { private static final StaticInnerSingleton INSTANCE = new StaticInnerSingleton(); } /** * 单例的构造函数必须永远是私有类型,以防止使用`new`运算符直接调用构造方法 */ private StaticInnerSingleton(){ } public static final StaticInnerSingleton getInstance() { return SingletonHolder.INSTANCE; } }
静态内部类实现的单例模式,既保证了线程安全,也保证了懒加载,同时不会由于加锁的方式致使性能开销过大。
这主要是由于 JVM 虚拟机能够保证多进程并发访问的正确性,即一个类的构造方法在多线程环境下,能够被正确加载。
所以,静态类内部类的实现方式很是推荐使用。
/** * CAS「AtomicReference」单例模式(线程安全) */ public class CompareAndSwapSingleton { private static final AtomicReference<CompareAndSwapSingleton> INSTANCE = new AtomicReference<CompareAndSwapSingleton>(); private static CompareAndSwapSingleton instance; /** * 单例的构造函数必须永远是私有类型,以防止使用`new`运算符直接调用构造方法 */ private CompareAndSwapSingleton() { } public static final CompareAndSwapSingleton getInstance() { for (; ; ) { CompareAndSwapSingleton instance = INSTANCE.get(); if (ObjectUtils.isNotEmpty(instance)) { return instance; } INSTANCE.compareAndSet(null, new CompareAndSwapSingleton()); return INSTANCE.get(); } } }
Java 并发库提供了不少原子类来支持并发访问的数据安全性:AtomicInteger
、 AtomicBoolean
、 AtomicLong
、AtomicReference
。
AtomicReference
能够封装引用一个实例,支持并发访问,该单例方式就是使用该特色实现。
采用 CAS 方式的优势就是不须要传统的加锁方式来保证线程安全,而是依赖 CAS 的忙等算法,依赖底层硬件的实现,来保证线程安全。相对于其余锁的实现没有线程的切换和阻塞,也就没有了额外开销,所以能够支持较大的并发。
固然,CAS 方式也存在缺点,忙等即若是一直没有获取到将会处于死循环中。
/** * 枚举单例(线程安全) */ public enum EnumSingleton { INSTANCE; public void whateverMethod() { System.out.println("enum singleton method"); } }
约书亚·布洛克(英语:Joshua J. Bloch,1961年8⽉28⽇-),美国著名程序员,《Effective Java》做者。他为Java平台设计并实做了许多的功能,曾担任Google的⾸席Java架构师(Chief Java Architect)。
《Effective Java》做者约书亚·布洛克推荐使用枚举的方式解决单例模式,这种方式应该是平时最少用到的。
这种方式解决的单例模式的主要问题:线程安全、自由串行化、单一实例。
调用方式
@Test public void testEnumSingleton() { EnumSingleton.INSTANCE.whateverMethod(); }
这种写法在功能上与共有域⽅法相近,可是它更简洁,⽆偿地提供了串⾏化机制,绝对防⽌对此实例化,即便是在⾯对复杂的串⾏化或者反射攻击的时候。虽然这种方式尚未⼴泛采⽤,可是单元素的枚举类型已经成为实现单例 的最佳⽅法。
但这种⽅式在存在继承场景下是不可⽤的。
getInstance
获取实例的静态方法来返回其所属类的一个相同实例。获取实例
方法必须是获取单例对象的惟一方式。设计模式并不难学,其自己就是多年经验提炼出的开发指导思想,关键在于多加练习,带着使用设计模式的思想去优化代码,就能构建出更合理的代码。
源码地址: https://github.com/yiyufxst/d...参考资料:
小博哥重学设计模式:https://github.com/fuzhengwei...
深刻设计模式:https://refactoringguru.cn/de...