本文仅供学习交流使用,侵权必删。
不作商业用途,转载请注明出处。
WHAT 什么是单例
如果一个类只允许创建一个对象或者实例,那么这个类就是一个单例类。这种方式就是单例设计模式。
单例存在的问题
- 单例违背面向对象的抽象特性。单例的实现并非基于接口而非实现的设计原则
- 单例违背面向对象的继承特性。因为构造函数已经私有化,子类无法通过super关键字来调用父构造方法
WHY 为什么使用单例
单例的存在能够提高对象的复用性,减少内存资源不必要的占用。某些业务场景下,我们需要某个组件是单独存在的。例如id生成器,我们希望只有一个实例生产id。如果存在多个id生成器实例,可能会存在id重复问题。
HOW 单例如何实现
饿汉式
将单例提前初始化,不存在线程安全问题。但提前占用内存资源,不能做到使用的时候才进行实例化。不支持延迟加载
public class Singleton {
private final static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
public void execute() {
// todo
}
}
懒汉式
使用synchronized保证了线程安全的问题,支持延迟加载。但因为锁粒度较大,每次有线程访问都需要锁的话,会造成一定的性能影响
public class Singleton {
private Singleton() {
}
private static Singleton instance;
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
private void execute(){
// todo
}
}
双重检测
双重校验:支持延迟加载,锁的粒度比懒汉式小,不会每次获取实例都需要加锁。此外单例需要使用volatile修饰,防止指令重排序导致调用方获取到未初始化完成的对象。
public class Singleton {
private Singleton() {
}
private static volatile Singleton instance = null;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
public void execute() {
// todo
}
}
静态内部类
利用JVM类加载机制,内部静态类Holder只有被访问时才会被创建实例对象,只有Holder的实例对象被创建,静态instance才会被创建。通过调用getInstance方法控制Holder类的实例化,从而既能保证线程安全,又能支持延迟加载。
public class Singleton {
private Singleton() {
}
private static class InstanceHolder {
private final static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return InstanceHolder.instance;
}
public voidexecute() {
//todo
}
}
枚举
利用JAVA枚举类型的特性,保证了线程安全和实例唯一。枚举能保证无法通过反射和序列化方式破解单例模式,能做到真正的单例。
public enum Singleton {
INSTANCE;
public void execute() {
//todo
}
}