单例模式的几种实现
所谓单例,就是整个程序有且仅有一个实例。该类负责创建自己的对象,同时确保只有一个对象被创建。在Java,一般常用在工具类的实现。
特点
- 类构造器私有
- 持有自己类型的属性
- 对外提供获取实例的静态方法
实现方式:
饿汉模式
线程安全,比较常用,但是会浪费空间,因为一开始就初始化
public class Singleton{
private Singleton(){}
private final static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
}
懒汉模式
线程不安全,延迟初始化,容易被反射攻击
public class Singleton{
private Singleton(){}
private static Singleton instance = null;
public static Singleton getInstance(){
if (instance == null) {
instance = new LazyMan();
}
return instance;
}
}
懒汉模式:双重校验锁
public class Singleton{
private Singleton(){}
// 双重锁必须加 volatile
private volatile static Singleton instance = null;
public static Singleton getInstance(){
if (instance == null) {
synchronized (Singleton.class){
if(instance == null){
instance = new Singleton(); // 不是原子操作
/**
* 1. 分配内存
* 2. 执行构造
* 3. 对象指向内存
* 可能发生重排
*/
}
}
}
return instance;
}
}
双重检查模式,进行了两次的判断,第一次是为了避免不要的实例,第二次是为了进行同步,避免多线程问题。由于singleton=new Singleton()
对象的创建在JVM中可能会进行重排序,在多线程访问下存在风险,使用volatile
修饰signleton
实例变量有效,解决该问题。
懒汉模式:静态内部类
public class Singleton{
private Singleton(){}
private static class Inner{
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return Inner.instance
}
}
第一次调用getInstance
方法时,虚拟机才加载 Inner 并初始化instance。
通过反射破坏:
Singleton instance = Singleton.getInstance();
Constructor<Singleton> declaredConstructor = Singleton.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Singleton instance2 = declaredConstructor.newInstance();
System.out.println(instance == instance2); //false
懒汉模式:枚举
枚举本身是一个类
// 枚举方式 极为推荐的方式
enum Singleton {
INSTANCE; //定义一个枚举的元素,就代表一个实例
//一个enum的构造方法限制是private的
public static Singleton getInstance() {
return INSTANCE;
}
}
使用:
Singleton instance = Singleton.INSTANCE;
Singleton instance = Singleton.getInstance();