单例模式
1. 单例介绍
单例模式:某个类的对象,无论创建多少次,都只存在一个实例
2. 实现方式
2-1. 饿汉式
饿汉式:当类加载
的时候,该单实例对象会被创建
静态变量方式
public class Singleton {
//私有构造方法
private Singleton() {}
//在成员位置创建该类的对象
private static Singleton instance = new Singleton();
//对外提供静态方法获取该对象
public static Singleton getInstance() {
return instance;
}
}
静态代码块方式
public class Singleton {
//私有构造方法
private Singleton() {}
//在成员位置创建该类的对象
private static Singleton instance;
static {
instance = new Singleton();
}
//对外提供静态方法获取该对象
public static Singleton getInstance() {
return instance;
}
}
2-2. 懒汉式
懒汉式:当首次使用该对象
时,该单实例对象会被创建
线程不安全
public class Singleton {
//私有构造方法
private Singleton() {}
//在成员位置创建该类的对象
private static Singleton instance;
//对外提供静态方法获取该对象
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
线程安全 - synchronized
public class Singleton {
//私有构造方法
private Singleton() {}
//在成员位置创建该类的对象
private static Singleton instance;
//对外提供静态方法获取该对象
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
线程安全 - 双重检查锁 + volatile
public class Singleton {
//私有构造方法
private Singleton() {}
private static volatile Singleton instance;
//对外提供静态方法获取该对象
public static Singleton getInstance() {
//第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实际
if(instance == null) {
synchronized (Singleton.class) {
//抢到锁之后再次判断是否为空
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
- 如果没有
volatile
,可能会导致instance
空指针异常 instance = new Singleton();
这条语句并非原子操作,它被编译器分为三步:- 为Singleton对象分配内存空间
- 调用Singleton的构造函数,初始化成员字段
- 将instance变量指向分配的内存地址
- JIT及时编译器存在
指令重排序
的优化,可能将步骤2和步骤3反过来执行。 - 如果线程A在执行instance = new Singleton();时先将instance指向一个未完成初始化的对象,此时如果线程B在执行if (instance == null)这个判断,会发现instance不为null,直接返回了instance,而此时这个instance可能还没有完成初始化,这样线程B就会访问到一个没有完全初始化的对象,如果去调用对象的成员,就容易抛出空指针异常。
- 加了
volatile
后,对于volatile变量的写入操作都会建立一个内存屏障
,防止指令重排序。
线程安全 - 静态内部类
public class Singleton {
//私有构造方法
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
//对外提供静态方法获取该对象
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
- JVM 在加载外部类的过程中, 是不会加载静态内部类的
- 只有内部类的
属性/方法被调用时
才会被加载,并初始化其静态属性。
3. 破坏单例
3-1. 序列化反序列化
解决方案
- 在Singleton类中添加
readResolve()
方法,在反序列化时被反射调用,如果定义了这个方法,就自动返回这个方法的值,如果没有定义,则返回新new出来的对象。
public class Singleton implements Serializable {
//私有构造方法
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
//对外提供静态方法获取该对象
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
/**
* 下面是为了解决序列化反序列化破解单例模式
*/
private Object readResolve() {
return SingletonHolder.INSTANCE;
}
}
3-2. 反射
解决方案
- 在构造方法中,判断单例对象是否为空。不为空则抛出异常
public class Singleton {
//私有构造方法
private Singleton() {
/*
反射破解单例模式需要添加的代码
*/
if(instance != null) {
throw new RuntimeException();
}
}
private static volatile Singleton instance;
//对外提供静态方法获取该对象
public static Singleton getInstance() {
if(instance != null) {
return instance;
}
synchronized (Singleton.class) {
if(instance != null) {
return instance;
}
instance = new Singleton();
return instance;
}
}
}