单例模式主要是为了避免因为创建了多个实例造成资源的浪费,且多个实例由于多次调用容易导致结果出现错误,使用单例模式能够保证整个应用中有且只有一个实例。
- 定义: 只需要三步就可以保证对象的唯一性
- 私有化构造函数
- 类内创建对象
- 对外提供一个可以让其他程序获取该对象的方法
常常被用作工具类(-Util/-Helper)。
饿汉式
1 2 3 4 5 6 7 8 9 10
| public class Singleton {
private Singleton() {}
private static final Singleton INSTANCE = new Singleton();
public static Singleton getInstance() { return INSTANCE; } }
|
优点:从它的实现中我们可以看到,这种方式的实现比较简单,在类加载的时候就完成了实例化,避免了线程的同步问题。
缺点:由于在类加载的时候就实例化了,所以没有达到Lazy Loading(懒加载)的效果,也就是说可能我没有用到这个实例,但是它也会加载,会造成内存的浪费。
懒汉
创建一个懒汉模式的单例,延迟实例的创建。
1 2 3 4 5 6 7 8 9 10 11 12
| public class Singleton { private Singleton() {} private static Singleton instance; public static Singleton getInstance() { if (instance == null) { instance = new singleton(); } return instance; } }
|
单例模式的懒汉模式.
这里的线程是不安全的,可能得到两个不同的实例。
修复线程安全问题,改进版本1:
1 2 3 4 5 6 7 8 9 10 11 12
| public class Singleton { private Singleton() {} private static Singleton instance;
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
|
懒汉式线程安全,通过synchronized
关键字控制,但效率低,因为synchronized
会🔓住该对象。
缺点: 效率太低了,每个线程在想获得类的实例时候,执行getInstance()
方法都要进行同步操作,而其实这个方法只执行一次实例化就够了。
改进版本2:
1 2 3 4 5 6 7 8 9 10 11 12
| public class Singleton { private Singleton() {} private static Singleton instance; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { instance = new Singleton(); } } return instance; } }
|
线程不安全,不可用。虽然加了🔓,但注意这里锁的是该类,不是对象实例。等到第一个线程执行完instance = new Singleton()
跳出这个🔓时,另一个进入if
语句的线程同样会实例化另外一个Singleton对象。
改进版本3: 双重校验🔓,新版jvm不再推荐
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class Singleton { private Singleton() {} private static Singleton instance; private static final Object MUTEX = new Object(); public static Singleton getInstance() { if (instance == null) { synchronized (MUTEX) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
|
双重校验锁(Double-checked locking),不再推荐使用,参考cwe-609。
改进版本4: 内部类写法【推荐】。也是其他jvm衍生语言的语法糖,譬如kotlin/scala/groovy的object
数据类型。
1 2 3 4 5 6 7 8 9
| public class Singleton { public static Singleton getInstance() { return SingletonHolder.INSTANCE; } private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } }
|
内部类会被延迟构建。
优点: 避免了线程不安全的出现,延迟加载,效率高。
改进版本5: 枚举【推荐】。
1 2 3 4 5
| public class Singleton { INSTANCE; private Singleton() {} }
|
借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
改进版本6: 原子类
1 2 3 4 5 6 7 8 9 10 11 12
| public class Singleton { private Singleton() {} private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<>(); public static Singleton getInstance() { if (INSTANCE.get() == null) { INSTANCE.compareAndSet(null, new Singleton()); } return INSTANCE.get(); } }
|
不太常见的实现方式,更多的是用作表示状态。对实例有一定的容忍度。无🔓设计,由CAS自旋等待。
改进版本7: 锁核原子类
1 2 3 4 5 6 7 8 9 10 11
| public class Singleton { private Singleton() {} private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<>(); public static Singleton getInstance() { while (INSTANCE.get() == null) { INSTANCE.compareAndSet(null, new Singleton()); } return INSTANCE.get(); } }
|
不具有容忍度的单例实现,一定要对象实例的存在才往下执行。