单例设计模式-容器单例
生活随笔
收集整理的這篇文章主要介紹了
单例设计模式-容器单例
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
基于容器的單例模式,與享元模式類似,我們也可以使用容器單例模式,來管理多個單例對象,那我們通過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 {@Overridepublic 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();}
}
?
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的单例设计模式-容器单例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 单例设计模式-Enum枚举单例、原理源码
- 下一篇: 单例设计模式-ThreadLocal线程