单例模式 定义 保证一个类仅有一个实例,并提供一个全局访问点。
类型 创建型
适用场景 1 2 想确保任何情况下都绝对只有一个实例 - 数据库连接池、线程池以及计数器等
优点 1 2 3 4 5 6 1. 在内存里只有一个实例,减少了内存开销 - 特别是一个对象频繁的创建和销毁,而且在创建和销毁时性能又不能很好的优化 2. 可以避免对资源的多重占用 - 如对一个文件进行写操作,由于只有一个实例存在内存中,可以避免对同一个资源文件同时写操作 3. 设置全局访问点,严格控制访问 - 对外控制创建的入口
缺点
拓展点 1 2 3 4 5 6 7 8 9 10 11 12 1. 私有构造器 - 为了禁止从单例类外部调用构造函数创建对象,为了达到目的必须设置构造函数为私有的 2. 线程安全 - 线程安全在单例模式设计的过程中非常重要 3. 延迟加载 - 延时创建对象 4. 序列化和反序列化安全 - 对于单例对象一旦序列化和反序列化,就会对单例进行破坏 5. 反射 - 单例模式也要防止反射攻击 6. 双重检锁机制 7. 单例静态内部类的实现方案
单例模式相关的设计模式 单例模式和工厂模式
单例模式和享元模式
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 package com.design.pattern.singleton.lazynosafe;public class LazySingleton { private static LazySingleton lazySingleton = null ; private LazySingleton () {} public static LazySingleton getInstance () { if (lazySingleton == null ){ lazySingleton = new LazySingleton(); } return lazySingleton; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.design.pattern.singleton.lazynosafe;import lombok.extern.slf4j.Slf4j;@Slf 4jpublic class MyThread implements Runnable { @Override public void run () { LazySingleton lazySingleton = LazySingleton.getInstance(); log.info(Thread.currentThread().getName() +" " + lazySingleton); } }
懒汉式单例模式安全-锁粒度较大
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 package com.design.pattern.singleton.lazysafebutlockisbig;public class LazySingleton { private static LazySingleton lazySingleton = null ; private LazySingleton () {} public synchronized static LazySingleton getInstance () { if (lazySingleton == null ){ lazySingleton = new LazySingleton(); } return lazySingleton; } }
懒汉式单例模式-双重检锁
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 package com.design.pattern.singleton.lazysafedoublecheck;public class LazyDoubleCheckSingleton { private volatile static LazyDoubleCheckSingleton lazySingleton = null ; private LazyDoubleCheckSingleton () {} public static LazyDoubleCheckSingleton getInstance () { if (lazySingleton == null ){ synchronized (LazyDoubleCheckSingleton.class ) { if (lazySingleton == null ){ lazySingleton = new LazyDoubleCheckSingleton(); } } } return lazySingleton; } }
枚举式单例模式
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 package com.design.pattern.singleton.enuminstance;public enum EnumInstance { INSTANCE; private Object data; public Object getData () { return data; } public void setData (Object data) { this .data = data; } public static EnumInstance getInstance () { return 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 package com.design.pattern.singleton.enuminstance;import lombok.extern.slf4j.Slf4j;import java.io.*;import java.lang.reflect.Constructor;@Slf 4jpublic class EnumInstanceTest { public static void main (String[] args) { EnumInstance instance = EnumInstance.getInstance(); instance.setData(new Object()); try { File file = new File("singleton" ); ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file)); objectOutputStream.writeObject(instance); ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file)); EnumInstance newInstance = (EnumInstance) objectInputStream.readObject(); log.info("instance: " + instance); log.info("newInstance: " + newInstance); log.info(String.format("instance [%s] newInstance" , instance == newInstance)); System.out.println("------------------------------------------" ); log.info("instance: " + instance.getData()); log.info("newInstance: " + newInstance.getData()); log.info(String.format("instance.data [%s] newInstance.data" , instance.getData() == newInstance.getData())); Class enumClass = EnumInstance.class ; Constructor constructor = enumClass.getDeclaredConstructor(); constructor.setAccessible(true ); } catch (Exception e) { log.info(e.getMessage()); } } }
容器单例模式
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 package com.design.pattern.singleton.containersingleton;import org.apache.commons.lang3.StringUtils;import org.springframework.util.ObjectUtils;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;public class ContainerSingleton { private static Map<String,Object> SINGLETON_MAP = new ConcurrentHashMap<>(); public static void putInstance (String key,Object instance) { if (StringUtils.isNotBlank(key) && !ObjectUtils.isEmpty(instance)){ if (!SINGLETON_MAP.containsKey(key)){ SINGLETON_MAP.put(key,instance); } } } public static Object getInstance (String key) { return SINGLETON_MAP.get(key); } }
饿汉式单例模式
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 package com.design.pattern.singleton.hungry;public class HungrySingleton { private HungrySingleton () { } private final static HungrySingleton singleton; static { singleton = new HungrySingleton(); } public static HungrySingleton getInstance () { return singleton; } }
破坏饿汉式单例-方式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 package com.design.pattern.singleton.reflectattackresolve;public class HungrySingleton { private final static HungrySingleton singleton; private HungrySingleton () { if (singleton == null ){ System.out.println("调用构造方法在赋值引用给singleton之前,这是指令重排序带来的可能" ); }else { System.out.println("调用构造方法在赋值引用给singleton之后,这是指令重排序带来的可能" ); } } static { singleton = new HungrySingleton(); } public static HungrySingleton getInstance () { return singleton; } }
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 package com.design.pattern.singleton.reflectattackresolve;import lombok.extern.slf4j.Slf4j;import java.lang.reflect.Constructor;@Slf 4jpublic class HungrySingletonTest { public static void main (String[] args) { try { Class objectClass = HungrySingleton.class ; HungrySingleton instance = HungrySingleton.getInstance(); Constructor constructor = objectClass.getDeclaredConstructor(); constructor.setAccessible(true ); HungrySingleton newInstance = (HungrySingleton) constructor.newInstance(); log.info("instance: " + instance); log.info("newInstance: " + newInstance); log.info(String.format("instance [%s] newInstance" ,instance == newInstance)); }catch (Exception e){ log.info(e.getMessage()); } } }
破坏饿汉式单例-方式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 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 package com.design.pattern.singleton.serializationdestroysingleton;import java.io.Serializable;public class HungrySingleton implements Serializable { private static final long serialVersionUID = 1136799709809340054L ; private HungrySingleton () { } private final static HungrySingleton singleton; static { singleton = new HungrySingleton(); } public static HungrySingleton getInstance () { return singleton; } public Object readResolve () { return singleton; } }
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 package com.design.pattern.singleton.serializationdestroysingleton;import lombok.extern.slf4j.Slf4j;import java.io.*;@Slf 4jpublic class HungrySingletonTest { public static void main (String[] args) { HungrySingleton instance = HungrySingleton.getInstance(); try { File file = new File("singleton" ); ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file)); objectOutputStream.writeObject(instance); ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file)); HungrySingleton newInstance = (HungrySingleton) objectInputStream.readObject(); log.info("instance: " + instance); log.info("newInstance: " + newInstance); log.info(String.format("instance [%s] newInstance" , instance == newInstance)); } catch (Exception e) { } } }
静态内部类的单例模式
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 package com.design.pattern.singleton.staticinnerclass;public class StaticInnerClassSingleton { private StaticInnerClassSingleton () {} private static class InnerClass { private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton(); } public static StaticInnerClassSingleton getInstance () { return InnerClass.staticInnerClassSingleton; } }
单例模式在源码中的使用 jdk-RunTime
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 package java.lang;import java.io.*;import java.util.StringTokenizer;import sun.reflect.CallerSensitive;import sun.reflect.Reflection;public class Runtime { private static Runtime currentRuntime = new Runtime(); public static Runtime getRuntime () { return currentRuntime; } private Runtime () {} }
Spring-AbstractFactoryBean
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 @Override public final T getObject () throws Exception { if (isSingleton()) { return (this .initialized ? this .singletonInstance : getEarlySingletonInstance()); } else { return createInstance(); } } @SuppressWarnings ("unchecked" )private T getEarlySingletonInstance () throws Exception { Class<?>[] ifcs = getEarlySingletonInterfaces(); if (ifcs == null ) { throw new FactoryBeanNotInitializedException( getClass().getName() + " does not support circular references" ); } if (this .earlySingletonInstance == null ) { this .earlySingletonInstance = (T) Proxy.newProxyInstance( this .beanClassLoader, ifcs, new EarlySingletonInvocationHandler()); } return this .earlySingletonInstance; }
MyBatis-ErrorContext
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 ErrorContext { private static final String LINE_SEPARATOR = System.getProperty("line.separator" ,"\n" ); private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<>(); private ErrorContext stored; private String resource; private String activity; private String object; private String message; private String sql; private Throwable cause; private ErrorContext () { } public static ErrorContext instance () { ErrorContext context = LOCAL.get(); if (context == null ) { context = new ErrorContext(); LOCAL.set(context); } return context; } }