什么是单例模式?
单例保证一个对象JVM中只能有一个实例
这里提供一个双重锁校验单例,并且引出了序列化破坏的问题,本篇来说一下这个原理和解决
首先把上一篇的单例代码拿过来贴上:
/** * 双重锁校验的单例 */ public class DoubleLock implements Serializable{ public static volatile DoubleLock doubleLock = null;//volatile防止指令重排序,内存可见(缓存中的变化及时刷到主存,并且其他的内存失效,必须从主存获取) private DoubleLock(){ //构造器必须私有 不然直接new就可以创建 } public static DoubleLock getInstance(){ //第一次判断,假设会有好多线程,如果doubleLock没有被实例化,那么就会到下一步获取锁,只有一个能获取到, //如果已经实例化,那么直接返回了,减少除了初始化时之外的所有锁获取等待过程 if(doubleLock == null){ synchronized (DoubleLock.class){ //第二次判断是因为假设有两个线程A、B,两个同时通过了第一个if,然后A获取了锁,进入然后判断doubleLock是null,他就实例化了doubleLock,然后他出了锁, //这时候线程B经过等待A释放的锁,B获取锁了,如果没有第二个判断,那么他还是会去new DoubleLock(),再创建一个实例,所以为了防止这种情况,需要第二次判断 if(doubleLock == null){ //下面这句代码其实分为三步: //1.开辟内存分配给这个对象 //2.初始化对象 //3.将内存地址赋给虚拟机栈内存中的doubleLock变量 //注意上面这三步,第2步和第3步的顺序是随机的,这是计算机指令重排序的问题 //假设有两个线程,其中一个线程执行下面这行代码,如果第三步先执行了,就会把没有初始化的内存赋值给doubleLock //然后恰好这时候有另一个线程执行了第一个判断if(doubleLock == null),然后就会发现doubleLock指向了一个内存地址 //这另一个线程就直接返回了这个没有初始化的内存,所以要防止第2步和第3步重排序 doubleLock = new DoubleLock(); } } } return doubleLock; } }
使用上面这个代码进行序列化操作的时候,会出现不同的实例,举个例子:
import java.io.*; public class Test { public static void main(String[] args) throws IOException, ClassNotFoundException { DoubleLock doubleLock = DoubleLock.getInstance(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("doubleLock_obj")); oos.writeObject(doubleLock); File file = new File("doubleLock_obj"); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); DoubleLock newDoubleLock = (DoubleLock) ois.readObject(); System.out.println(doubleLock); System.out.println(newDoubleLock); System.out.println(doubleLock == newDoubleLock); } }
输出结果:(第一次运行会报错,这里略过不介绍了,再运行一次就好了)
com.ygz.designpatterns.singleton.DoubleLock@27c170f0
com.ygz.designpatterns.singleton.DoubleLock@5451c3a8
false
可以看到,经过一次输出输入流,产生了两个DoubleLock对象,这是为什么呢?我们点进去上面测试代码的ois.readObject()
方法,进入源码看:
java.io.ObjectInputStream中:
public final Object readObject() throws IOException, ClassNotFoundException { if (enableOverride) { return readObjectOverride(); } // if nested read, passHandle contains handle of enclosing object int outerHandle = passHandle; try { Object obj = readObject0(false); handles.markDependency(outerHandle, passHandle); ClassNotFoundException ex = handles.lookupException(passHandle); if (ex != null) { throw ex; } if (depth == 0) { vlist.doCallbacks(); } return obj; } finally { passHandle = outerHandle; if (closed && depth == 0) { clear(); } } }
再点进去readObject0(false)
方法:
这个方法其实就是读取对象的方法
private Object readObject0(boolean unshared) throws IOException { boolean oldMode = bin.getBlockDataMode(); if (oldMode) { int remain = bin.currentBlockRemaining(); if (remain > 0) { throw new OptionalDataException(remain); } else if (defaultDataEnd) { /* * Fix for 4360508: stream is currently at the end of a field * value block written via default serialization; since there * is no terminating TC_ENDBLOCKDATA tag, simulate * end-of-custom-data behavior explicitly. */ throw new OptionalDataException(true); } bin.setBlockDataMode(false); } byte tc; while ((tc = bin.peekByte()) == TC_RESET) { bin.readByte(); handleReset(); } depth++; try { switch (tc) { case TC_NULL: return readNull(); case TC_REFERENCE: return readHandle(unshared); case TC_CLASS: return readClass(unshared); case TC_CLASSDESC: case TC_PROXYCLASSDESC: return readClassDesc(unshared); case TC_STRING: case TC_LONGSTRING: return checkResolve(readString(unshared)); case TC_ARRAY: return checkResolve(readArray(unshared)); case TC_ENUM: return checkResolve(readEnum(unshared)); case TC_OBJECT: return checkResolve(readOrdinaryObject(unshared)); case TC_EXCEPTION: IOException ex = readFatalException(); throw new WriteAbortedException("writing aborted", ex); case TC_BLOCKDATA: case TC_BLOCKDATALONG: if (oldMode) { bin.setBlockDataMode(true); bin.peek(); // force header read throw new OptionalDataException( bin.currentBlockRemaining()); } else { throw new StreamCorruptedException( "unexpected block data"); } case TC_ENDBLOCKDATA: if (oldMode) { throw new OptionalDataException(true); } else { throw new StreamCorruptedException( "unexpected end of block data"); } default: throw new StreamCorruptedException( String.format("invalid type code: %02X", tc)); } } finally { depth--; bin.setBlockDataMode(oldMode); } }
因为我们是对Objcet进行操作,所以看
case TC_OBJECT: return checkResolve(readOrdinaryObject(unshared));
点进去看readOrdinaryObject(unshared)
方法:
private Object readOrdinaryObject(boolean unshared) throws IOException { if (bin.readByte() != TC_OBJECT) { throw new InternalError(); } ObjectStreamClass desc = readClassDesc(false); desc.checkDeserialize(); Class cl = desc.forClass(); if (cl == String.class || cl == Class.class || cl == ObjectStreamClass.class) { throw new InvalidClassException("invalid class descriptor"); } Object obj; try { obj = desc.isInstantiable() ? desc.newInstance() : null; } catch (Exception ex) { throw (IOException) new InvalidClassException( desc.forClass().getName(), "unable to create instance").initCause(ex); } passHandle = handles.assign(unshared ? unsharedMarker : obj); ClassNotFoundException resolveEx = desc.getResolveException(); if (resolveEx != null) { handles.markException(passHandle, resolveEx); } if (desc.isExternalizable()) { readExternalData((Externalizable) obj, desc); } else { readSerialData(obj, desc); } handles.finish(passHandle); if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) //☆☆★★这是一个标记,下面会提到这里 { Object rep = desc.invokeReadResolve(obj); if (unshared && rep.getClass().isArray()) { rep = cloneArray(rep); } if (rep != obj) { handles.setObject(passHandle, obj = rep); } } return obj; }
Java单例---序列化破坏单例模式原理解析(二)https://developer.aliyun.com/article/1393251