基于容器的单例模式,与享元模式类似,我们也可以使用容器单例模式,来管理多个单例对象,那我们通过coding,debug,
讲解的方式来学习一下
package com.learn.design.pattern.creational.singleton;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.Map;
/**
* 容器的单例类
* 那这个类实现起来非常简单
* 我们来操作一下
*
*
*
* @author Leon.Sun
*
*/
public class ContainerSingleton {
/**
* 我们没有写他的private构造器
* 这样我们直接调用静态方法
* 这种public的方法
* 就OK了
* 然来到Test里面
*
*
*/
private ContainerSingleton(){
}
/**
* 首先声明一个Map
* 我们的KEY使用一个String
* value就用Object
* 这个叫singletonMap
* new一个HashMap
* 这个泛型也写上
* 那这个map就写好了
* 本身这个Map作为缓存的话
* 要往里放对象
* 也好取对象
* 那很简单的
*
*
*/
private static Map<String,Object> singletonMap = new HashMap<String,Object>();
/**
* putInstance两个参数
* 一个是key
* 另外是instance
* 因为它是一个map
* 可以管理很多对象
* 然后做一个简单的判断
*
*
* @param key
* @param instance
*/
public static void putInstance(String key,Object instance){
/**
* 用blank来判断key
* 并且instance不为null
* 当这种情况下
* 我们才会往里边放
* 那在放之前呢
* 我们还要用map来判断一下
*
* Thread0进到这里面他开始判断
* 判断肯定正常
* key传的是object
* value也是一个object
* 注意它是@420
*
*
*/
if(StringUtils.isNotBlank(key) && instance != null){
/**
* singletonMap.containsKey取反
* 当不包含这个key的时候
* 我们才会真正的往里放
* put key和instance
* 那所谓的容器单例呢
* 就是通过这个map呢去实现单例对象的一个容器
* 那这里是保证key的合法性和唯一性
* 那接下来也很简单
*
* 进入到这里肯定是不存在的
* 因为map里面的size还是0
*
*
*/
if(!singletonMap.containsKey(key)){
/**
* 这一行先不动
* 切另一个线程
* 他也会同样的进来
* 那我们看一下这个instance
* 是449
* 另外一个instance是420
* 也就是说在放的时候
* 放了两个对象
* 但是他们的key是同一个
* 也就是说在初始化的时候
* 这个对象是有可能放多个的
* 那具体还要看线程的数量
* 但是一旦放置完成呢
* 我们在取的时候
* 就只会取一个呢
* 因为后执行的
* 把之前执行的
* 覆盖掉
* 因为他们的key一样的
* 那put ok了
* Thread0里面放的是420
* 我们直接F8过来
* 刚刚有说
* Thread0放的是420
* 我们打开这个map
* 我们可以看到map里面放的是420
* 我们再切回Thread1
* 执行put
* 然后F8
* Thread1也到getInstance这里呢
* 我们看一下map里面的值
* 里面已经变成449
* 也就是说后执行的Thread1
* 将要获取449这个Object
* 那我们再切回Thread0
* 看一下这个Map
* 他已经变成449了
* 你看上去这里将要返回同一个对象
* 但是有一点
* 我们现在在干预线程
* 如果不干预线程的话
* 例如Thread0先放置成功
* 他就立刻返回了
* Thread1才去放置
* Thead1返回
* 那这样的话他们返回的对象就不是同一个了
* 我们把断点取消
* 直接F8过
* 从结果看上Thread0和Thread1返回的都是同一个对象
* 但是他们中间还是有隐患的
* 所以这种单例模式适用场景之前也说了
* 根据实际的业务需要
* 我们再看一下这个类
* 我们想一下
* HashMap他本身就不是线程安全的
* 那如果我们把它改成Hashtable的话
* 会不会就变成线程安全呢
* 当然Hashtable是线程安全的
* 我们如果使用Hashtable的话
* 这种模式的单例模式就是线程安全的
* 但是我们频繁去取的时候
* 建议使用同步锁
* 不建议使用Hashtable
* 那我们可以折中一下
* 但是我们使用了静态的HashMap
* 而且直接操作了这个map
* 那在这种场景下呢
* ConCurrentHashMap并不是绝对的线程安全
* 所以我们不考虑安全机制的话
* 这种容器单例模式
* 也是有一定适用场景的
* 在安卓的SDK也用的比较多
* JDK中也有这种模式
* 那后边我们会讲解一些单例模式在源码中的应用
* JDK中使用容器解决单例模式的一个方案
* 和这个比较类似
* 所以在使用这种模式的时候
* 我建议它使用Hashtable
* 所以呢这种单例模式
* 是一个平衡
* 根据业务场景来判断
* 如果我们系统中单例模式非常多的话
* 我们也可以考虑用一个容器把单例
* 他的优点是统一管理
* map相当于一个缓存
* 确认是线程不安全
*
*
*
*/
singletonMap.put(key,instance);
}
}
}
/**
* 参数是key
*
*
* @param key
* @return
*/
public static Object getInstance(String key){
/**
* 从singletonMap直接get key就可以了
* 非常简单
* 这种写法非常时候容器在初始化的时候
* 我们就把多个单例对象放入到singletonMap里边
* 统一管理
* 那在使用的时候呢
* 通过key直接从map当中获取单例对象
* 那这里面我们也联想一下
* 假设我们认为singletonMap是一个容器的话
* 对于Spring容器
* Spring容器里讲的单例和我们现在讲的单例
* 是没有关系的
* 但是可以类比一下
* 我们从Spring容器里面取对象的时候
* 默认如果不配置的话
* 是一个单例的
* 那如果我们在类上加上注解
* 或者配置bean的属性为prototype属性的话
* 那他就变成一个多例的
* 但对于这一块我们先不深入讲解
* 那我们先回来接着说这个
* 那我们看一下
* 这里面我们使用HashMap
* 很明显的
* 如果用HashMap肯定不是线程安全的
* 但是如果我们使用他
* 在类初始化的时候
* 去把这个map初始化完成
* 也就是把所有的单例对象都生成完
* 放到这里边
* 用的时候直接用
* 这样是可以的
* 所以这种单例模式
* 还是要看具体的业务场景
* 那我们简单测试一下
* 还是用多线程debug来看一下
* 这种模式单例模式实现方案安全问题
* 首先我们打开Test这个类
* Test类比较多
* 我们看Singleton里面的
*
*
*
*/
return singletonMap.get(key);
}
}
package com.learn.design.pattern.creational.singleton;
public class T implements Runnable {
@Override
public void run() {
// LazySingleton lazySingleton = LazySingleton.getInstance();
// System.out.println(Thread.currentThread().getName()+" "+lazySingleton);
// LazyDoubleCheckSingleton instance = LazyDoubleCheckSingleton.getInstance();
// StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();;
ContainerSingleton.putInstance("object",new Object());
/**
* 从这里面取一下instance
* 以为这里调用的是静态方法
* 所以呢
* 回到这个类里面
*
*
*/
Object instance = ContainerSingleton.getInstance("object");
// ThreadLocalInstance instance = ThreadLocalInstance.getInstance();
System.out.println(Thread.currentThread().getName()+" "+instance);
}
}
package com.learn.design.pattern.creational.singleton;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// LazySingleton lazySingleton = LazySingleton.getInstance();
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
/**
* 现在是两个线程
* 我们把往容器放对象的这个过程
* 放到线程里边
* 让他们并发去放
* 来到T里边
*
*
*/
Thread t1 = new Thread(new T());
Thread t2 = new Thread(new T());
t1.start();
t2.start();
System.out.println("program end");
// HungrySingleton instance = HungrySingleton.getInstance();
// EnumInstance instance = EnumInstance.getInstance();
// instance.setData(new Object());
// ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
// oos.writeObject(instance);
// File file = new File("singleton_file");
// ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
// HungrySingleton newInstance = (HungrySingleton) ois.readObject();
// EnumInstance newInstance = (EnumInstance) ois.readObject();
// System.out.println(instance.getData());
// System.out.println(newInstance.getData());
// System.out.println(instance.getData() == newInstance.getData());
// Class objectClass = HungrySingleton.class;
// Class objectClass = StaticInnerClassSingleton.class;
// Class objectClass = LazySingleton.class;
// Class objectClass = EnumInstance.class;
////
// Constructor constructor = objectClass.getDeclaredConstructor();
// Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);
//
// constructor.setAccessible(true);
// EnumInstance instance = (EnumInstance) constructor.newInstance();
// EnumInstance instance = (EnumInstance) constructor.newInstance("Geely",666);
//
// LazySingleton newInstance = (LazySingleton) constructor.newInstance();
// LazySingleton instance = LazySingleton.getInstance();
// StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();
// StaticInnerClassSingleton newInstance = (StaticInnerClassSingleton) constructor.newInstance();
// HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
// HungrySingleton instance = HungrySingleton.getInstance();
// System.out.println(instance);
// System.out.println(newInstance);
// System.out.println(instance == newInstance);
// EnumInstance instance = EnumInstance.getInstance();
// instance.printTest();
}
}