1. 模式简介 确保一个类在任何情况下都绝对只有一个
实例,并提供一个全局的访问入口点;隐藏所有的构造方法;属于创建型设计模式
。
2. 实现方式 2.1. 饿汉式单例 先将实例创建出来,用的时候直接拿
2.1.1. 属性初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class EagerSingleton1 { private final static EagerSingleton1 INSTANCE = new EagerSingleton1(); private EagerSingleton1 () { } public static EagerSingleton1 getInstance () { return INSTANCE; } public static void main (String[] args) throws InterruptedException { ConcurrentExecutor.execute(() -> { System.out.println("线程号: " + Thread.currentThread().getName() + "," + EagerSingleton1.getInstance()); }, 10 , 5 ); } }
2.1.2. 静态代码块初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class EagerSingleton2 { private final static EagerSingleton2 INSTANCE; static { INSTANCE = new EagerSingleton2(); } private EagerSingleton2 () { } public static EagerSingleton2 getInstance () { return INSTANCE; } public static void main (String[] args) throws InterruptedException { ConcurrentExecutor.execute(() -> { System.out.println("线程号: " + Thread.currentThread().getName() + "," + EagerSingleton2.getInstance()); }, 10 , 5 ); } }
2.2. 懒汉式单例 用的时候再去创建
2.2.1. 简单实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class LazySingletonSimple { private static LazySingletonSimple INSTANCE = null ; private LazySingletonSimple () { } public synchronized static LazySingletonSimple getInstance () { if (INSTANCE == null ) { INSTANCE = new LazySingletonSimple(); } return INSTANCE; } public static void main (String[] args) throws InterruptedException { ConcurrentExecutor.execute(() -> { System.out.println("线程号: " + Thread.currentThread().getName() + "," + LazySingletonSimple.getInstance()); }, 10 , 5 ); } }
2.2.2. 双重检查锁实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class LazySingletonDoubleCheck { private volatile static LazySingletonDoubleCheck INSTANCE = null ; private LazySingletonDoubleCheck () { } public static LazySingletonDoubleCheck getInstance () { if (INSTANCE == null ) { synchronized (LazySingletonDoubleCheck.class) { if (INSTANCE == null ) { INSTANCE = new LazySingletonDoubleCheck(); } } } return INSTANCE; } public static void main (String[] args) throws InterruptedException { ConcurrentExecutor.execute(() -> { System.out.println("线程号: " + Thread.currentThread().getName() + "," + LazySingletonDoubleCheck.getInstance()); }, 10 , 5 ); } }
2.2.3. 静态内部类实现(推荐写法
) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class LazySingletonInnerClass { private LazySingletonInnerClass () { if (SingletonHolder.INSTANCE != null ) { throw new RuntimeException("不允许创建多个实例" ); } } public static LazySingletonInnerClass getInstance () { return SingletonHolder.INSTANCE; } private static class SingletonHolder { private final static LazySingletonInnerClass INSTANCE = new LazySingletonInnerClass(); } public static void main (String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { LazySingletonInnerClass instance1 = LazySingletonInnerClass.getInstance(); Class<LazySingletonInnerClass> clazz = LazySingletonInnerClass.class; Constructor<LazySingletonInnerClass> constructor = clazz.getDeclaredConstructor(null ); constructor.setAccessible(true ); LazySingletonInnerClass instance2 = constructor.newInstance(); System.out.println("instance1 = " + instance1); System.out.println("instance2 = " + instance2); } }
2.3. 注册式单例 将每一个实例都缓存到统一的容器中,使用唯一标识获取实例
2.3.1. 枚举式单例(推荐写法
) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public enum RegisterSingletonEnum { INSTANCE; public static RegisterSingletonEnum getInstance () { return INSTANCE; } public static void main (String[] args) throws InterruptedException { ConcurrentExecutor.execute(() -> { System.out.println("线程号: " + Thread.currentThread().getName() + "," + RegisterSingletonEnum.getInstance()); }, 10 , 5 ); } }
2.3.2. 容器式单例 2.3.2.1. Spring式实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class RegisterSingletonContainer { private static final Map<String, Object> BEAN_CACHE = new ConcurrentHashMap<>(); private RegisterSingletonContainer () { } public static Object getInstance (String className) { synchronized (BEAN_CACHE) { if (!BEAN_CACHE.containsKey(className)) { Class<?> aClass = null ; try { aClass = Class.forName(className); } catch (ClassNotFoundException e) { e.printStackTrace(); } BEAN_CACHE.put(className, aClass); return aClass; } else { return BEAN_CACHE.get(className); } } } public static void main (String[] args) throws InterruptedException { ConcurrentExecutor.execute(() -> { Object instance = RegisterSingletonContainer.getInstance("com.xkcoding.design.pattern.creational.singleton.register.RegisterSingletonContainer" ); System.out.println(System.currentTimeMillis() + " :: " + instance); }, 10 , 5 ); } }
2.3.2.2. ThreadLocal式单例 ThreadLocal式单例其实也属于 容器式单例
,伪线程安全,保证线程内部全部唯一,同一线程内线程安全
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class RegisterSingletonThreadLocal { private static final ThreadLocal<RegisterSingletonThreadLocal> INSTANCE = ThreadLocal.withInitial(RegisterSingletonThreadLocal::new ); private RegisterSingletonThreadLocal () { } public static RegisterSingletonThreadLocal getInstance () { return INSTANCE.get(); } public static void main (String[] args) throws InterruptedException { System.out.println("线程号: " + Thread.currentThread().getName() + "," + RegisterSingletonThreadLocal.getInstance()); System.out.println("线程号: " + Thread.currentThread().getName() + "," + RegisterSingletonThreadLocal.getInstance()); System.out.println("线程号: " + Thread.currentThread().getName() + "," + RegisterSingletonThreadLocal.getInstance()); System.out.println("线程号: " + Thread.currentThread().getName() + "," + RegisterSingletonThreadLocal.getInstance()); ConcurrentExecutor.execute(() -> { System.out.println("线程号: " + Thread.currentThread().getName() + "," + RegisterSingletonThreadLocal.getInstance()); }, 10 , 5 ); } }
3. 问题点 3.1. 反序列化破坏单例的情况 测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public class EagerSingleton3 implements Serializable { private final static EagerSingleton3 INSTANCE = new EagerSingleton3(); private EagerSingleton3 () { } public static EagerSingleton3 getInstance () { return INSTANCE; } private Object readResolve () { return INSTANCE; } public static void main (String[] args) throws IOException, ClassNotFoundException { EagerSingleton3 instance1 = EagerSingleton3.getInstance(); EagerSingleton3 instance2 = null ; FileOutputStream fos = new FileOutputStream("EagerSingleton3.obj" ); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(instance1); oos.flush(); oos.close(); FileInputStream fis = new FileInputStream("EagerSingleton3.obj" ); ObjectInputStream ois = new ObjectInputStream(fis); instance2 = (EagerSingleton3) ois.readObject(); ois.close(); System.out.println(instance1); System.out.println(instance2); System.out.println(instance1 == instance2); } }
其实上面的代码已经给出答案了,重写 readResolve()
方法,将 INSTANCE
返回即可解决。
为什么重写 readResolve()
方法,将 INSTANCE
返回就可以解决这个单例被破坏的问题呢?跟一波源码看看 ~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 Object obj = readObject0(false ); 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(); 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)); } case TC_OBJECT: return checkResolve(readOrdinaryObject(unshared));obj = desc.isInstantiable() ? desc.newInstance() : null ; 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) { if (rep != null ) { if (rep.getClass().isArray()) { filterCheck(rep.getClass(), Array.getLength(rep)); } else { filterCheck(rep.getClass(), -1 ); } } handles.setObject(passHandle, obj = rep); } } boolean hasReadResolveMethod () { requireInitialized(); return (readResolveMethod != null ); } private Method readResolveMethod;readResolveMethod = getInheritableMethod(cl, "readResolve" , null , Object.class); private Object readResolve () { return INSTANCE; }
注意哦,当我们使用枚举式单例的写法就不会出现这种情况,具体原因如下:
1 2 3 4 5 6 7 case TC_ENUM: return checkResolve(readEnum(unshared));Enum<?> en = Enum.valueOf((Class)cl, name);
3.2. 反射破坏单例的情况 测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class LazySingletonInnerClass { private LazySingletonInnerClass () { if (SingletonHolder.INSTANCE != null ) { throw new RuntimeException("不允许创建多个实例" ); } } public static LazySingletonInnerClass getInstance () { return SingletonHolder.INSTANCE; } private static class SingletonHolder { private final static LazySingletonInnerClass INSTANCE = new LazySingletonInnerClass(); } public static void main (String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { LazySingletonInnerClass instance1 = LazySingletonInnerClass.getInstance(); Class<LazySingletonInnerClass> clazz = LazySingletonInnerClass.class; Constructor<LazySingletonInnerClass> constructor = clazz.getDeclaredConstructor(null ); constructor.setAccessible(true ); LazySingletonInnerClass instance2 = constructor.newInstance(); System.out.println("instance1 = " + instance1); System.out.println("instance2 = " + instance2); } }
其实上面的代码已经给出答案了,只需要在构造方法里判断当前实例是否已创建,已创建抛出运行时异常,即可解决。
4. 应用 5. 优缺点 优点: 在内存中只有一个实例,减少内存开销;可以避免对资源的多重占用;设置全局访问点,严格控制访问
缺点: 没有接口,扩展困难;扩展单例对象,只能修改代码,不符合开闭原则
6. 完整代码地址 https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/creational/singleton
测试代码里 ConcurrentExecutor
具体实现请看这里:https://github.com/xkcoding/design-pattern/blob/master/src/main/java/com/xkcoding/design/pattern/utils/ConcurrentExecutor.java