java设计模式——单例模式(三)
容器单例模式
之前学习Structs2,Spring框架时,经常会听到单例,多例。虽然这与单例模式不太一样,但是都很类似。在程序运行的时候,就加载所有的实例,然后用的时候直接取出
看下面代码:
/** * @program: designModel * @description: 容器单例模式 * @author: YuKai Fan * @create: 2018-12-11 14:59 **/ public class ContaineSingleton { private ContaineSingleton() {} private static Map<String, Object> singletonMap = new HashMap<String, Object>(); public static void putInstance(String key, Object instance) { if (StringUtils.isNotBlank(key) && instance != null) { if (!singletonMap.containsKey(key)) { singletonMap.put(key, instance); } } } public static Object getInstance(String key) { return singletonMap.get(key); } }
但是,这种方式在不考虑序列化与反射的情况下,依旧是不安全的。因为在多线程的环境下,还是会产生不同的实例,这需要结合场景来使用。
如果使用hashTable来保证线程安全的话,效率会很低,频繁去取实例都回家加同步锁。如果使用ConcurrentHashMap,由于被static修饰,相当于直接操作了 map,在这种场景下,也不是绝对的线程安全。
ThreadLocal环境下的"单例模式"
这种带引号的单例模式,并不是真正的单例模式。因为并不能保证整个应用只产生一个实例,只会保证整个应用线程唯一
/** * @program: designModel * @description: * @author: YuKai Fan * @create: 2018-12-11 15:17 **/ public class ThreadLocalInstance { private static final ThreadLocal<ThreadLocalInstance> threadLocalInstance = new ThreadLocal<ThreadLocalInstance>(){ @Override protected ThreadLocalInstance initialValue() { return new ThreadLocalInstance(); } }; private ThreadLocalInstance() {} public static ThreadLocalInstance getInstance() { return threadLocalInstance.get(); } }
/** * @program: designModel * @description: * @author: YuKai Fan * @create: 2018-12-04 14:11 **/ public class T implements Runnable { public void run() { ThreadLocalInstance instance = ThreadLocalInstance.getInstance(); System.out.println(Thread.currentThread().getName() + "" + instance); } }
/** * @program: designModel * @description: * @author: YuKai Fan * @create: 2018-12-04 14:07 **/ public class Test { public static void main(String[] args) throws IOException, ClassNotFoundException { // LazySingleton lazySingleton = LazySingleton.getInstance(); Thread t1 = new Thread(new T()); Thread t2 = new Thread(new T()); t1.start(); t2.start(); System.out.println("测试"); } }
输出结果:
产生的是不同的实例,但是ThreadLocal回味每个线程提供独立的变量副本,将每个线程的实例隔离。保证在多线程的情况下,会保证每个线程只能产生一个实例
单例模式源码分析
单例模式在jdk下的应用:
java.lang.Runtime下的getRuntime()方法,属于饿汉式
AWT下的Desktop类中的getDesktop()——容器单例的影子
Spring中的单例与之前介绍的单例不一样,
spring中的单例是在bean作用域中的一个,这个作用域在每个应用程序上下文中仅创建一个我们设置单例属性的这个实例;与之前介绍的单例模式最大的区别在于Spring讲实例的数量限制在每个应用程序的上下文,而之前的单例模式中的实例,是将数量限制在给定的类加载器管理的整个空间里。所以Spring容器中,即使设置属性为单例,都可以拿出来这个对象。
在AbstractFactoryBean中的getObject()方法,可以看到单例模式的影子。
MyBatis中的单例模式:
ErrorContext类,保证每个线程的各自数据,使用的就是单例模式,也就是ThreadLocal的单例模式。