目录
写在前面
本文从最基础的饿汉式及懒汉式demo进行引入,通过jdk源码分别分析了:反射及反序列化破坏单例原理、readResolve()如何防止反序列化破坏单例、枚举式单例的优点及如何防止反射及反序列化破坏、以及spring容器式单例思想详解。
饿汉式单例模式:
一般形式
/**
* 优点:执行效率高,性能高,没有任何的锁
* 缺点:某些情况下,可能会造成内存浪费
*/
public class HungrySingleton {
private static final HungrySingleton hungrySingleton = new HungrySingleton();
private HungrySingleton(){
}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
静态代码块
/**
* 静态代码块
*/
public class HungryStaticSingleton {
private static final HungryStaticSingleton hungrySingleton;
static {
hungrySingleton = new HungryStaticSingleton();
}
private HungryStaticSingleton(){
}
public static HungryStaticSingleton getInstance(){
return hungrySingleton;
}
}
懒汉式单例模式:
双重检查锁
/**
* 优点:性能高了,线程安全了
* 缺点:可读性难度加大,不够优雅,并且加锁会产生性能问题
*/
public class LazyDoubleCheckSingleton {
private volatile static LazyDoubleCheckSingleton instance;
private LazyDoubleCheckSingleton(){
}
public static LazyDoubleCheckSingleton getInstance(){
//检查是否要阻塞
if (instance == null) {
synchronized (LazyDoubleCheckSingleton.class) {
//检查是否要重新创建实例
if (instance == null) {
instance = new LazyDoubleCheckSingleton();
//指令重排序的问题
}
}
}
return instance;
}
}
静态内部类
/*
优点:写法优雅,利用了Java本身语法特点,性能高,避免了内存浪费,不能被反射破坏
缺点:不优雅
*/
public class LazyStaticInnerClassSingleton {
private LazyStaticInnerClassSingleton(){
if(LazyHolder.INSTANCE != null){
throw new RuntimeException("不允许非法访问");
}
}
private static LazyStaticInnerClassSingleton getInstance(){
return LazyHolder.INSTANCE;
}
private static class LazyHolder{
private static final LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton();
}
}
反射破坏单例
public class ReflectTest {
public static void main(String[] args) {
try {
//获取单例类的class及构造器
Class<?> clazz = LazyDoubleCheckSingleton.class;
Constructor c = clazz.getDeclaredConstructor(null);
//设置强制访问
c.setAccessible(true);
//实例化两次
Object instance1 = c.newInstance();
Object instance2 = c.newInstance();
//分别打印
System.out.println(instance1);
System.out.println(instance2);
//false
System.out.println(instance1 == instance2);
}catch (Exception e){
e.printStackTrace();
}
}
}
输出结果:
序列化破坏单例:
//一个单例对象创建好后,有时候需要将对象序列化然后写入磁盘,下次使用时再从磁盘中读取对象进行反序列化,然后将其转化为内存对象。反序列化后的对象会重新分配内存,即重新创建
public class SeriableSingletonTest {
public