Bootstrap

序列化反序列化破坏单例模式的原因及解决方案(以懒汉式为例)

普通懒汉式

/**
 * 懒汉式单例
 */
@Data
public class LazySingle implements Serializable {
    private String singleName;
    //volatile,确保本条指令不会因编译器的优化而省略,且要求每次直接读值
    private static volatile LazySingle lazySingle=null;

    //解决多线程不能保证单例情况   **Double Check(双重校验) + Lock(加锁)**
    public static LazySingle getInstance() {
        if(lazySingle==null){// 线程A和线程B同时看到singleton = null,如果不为null,则直接返回singleton
            synchronized (LazySingle.class) {// 线程A或线程B获得该锁进行初始化
                if(lazySingle == null){// 其中一个线程进入该分支,另外一个线程则不会进入该分支
                    lazySingle = new LazySingle();
                }
            }

        }
        return lazySingle;
    }

使用序列化与反序列化破坏单例模式

 try {
            File file = new File("Singleton.txt");
            //创建输出流
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
            //将单例对象写到文件中  序列化
            oos.writeObject(LazySingle.getInstance());
            //从文件读取单例对象
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
            //反序列化得到对象lazySingle
            LazySingle lazySingle=(LazySingle)ois.readObject();
            System.out.println(lazySingle==LazySingle.getInstance()); //false
        } catch (Exception e) {
            e.printStackTrace();
        }

原因

通过阅读源码发现,反序列化方法readObject()readObject0()中如果是Object对象则调用readOrdinaryObject(),该方法首先会通过一个三目运算来创建序列化的对象。如果这个对象能实例化就创建一个新对象

obj = desc.isInstantiable() ? desc.newInstance() : null;

解决方法

在通过三目运算创建了对象之后,还会去找这个对象里是否有readResolve()方法,如果有,则通过这方法返回对象。

    if (obj != null &&handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()){
    	 Object rep = desc.invokeReadResolve(obj);
   }

所以只需要在单例类中新增readResolve()方法即可

	public Object readResolve() {
	    return getInstance();
	}
;