細說Java中的幾種單例模式

在Java中慌随,單例模式分為很多種团甲,本人所了解的單例模式有以下幾種溉贿,如有不全還請大家留言指點:

  • 餓漢式
  • 懶漢式/Double check(雙重檢索)
  • 靜態(tài)內(nèi)部類
  • 枚舉單例

一仑嗅、餓漢式

image

餓漢式是在jvm加載這個單例類的時候畦浓,就會初始化這個類中的實例痹束,在使用單例中的實例時直接拿來使用就好,因為加載這個類的時候就已經(jīng)完成初始化讶请,并且由于是已經(jīng)加載好的單例實例因此是線程安全的祷嘶,并發(fā)獲取的情況下不會有問題,是一種可投入使用的可靠單例夺溢。

優(yōu)點:使用起來效率高论巍、線程安全

缺點:由于jvm在加載單例類的時候需要初始化單例實例,因此在加載單例的時候針對jvm內(nèi)存不夠友好风响。

二嘉汰、懶漢式

image

最簡單的懶漢式,核心思想就是彌補餓漢式的缺點状勤,在jvm加載單例類的時候不去初始化實例鞋怀,而是在第一次獲取實例的時候再去初始化實例。但是這樣理論完美的單例在使用的時候有一個致命的缺點持搜,在多線程使用的情況下密似,有時會出現(xiàn)不同線程從單例實例中獲取不同的實體。針對多線程環(huán)境中并不可靠葫盼。

優(yōu)點:針對jvm內(nèi)存比較友好辛友,實現(xiàn)了實例的懶加載。

缺點:多線程環(huán)境下不安全剪返,會出現(xiàn)不同線程從單例實例中獲取不同的實體的情況废累。

具體為什么會出現(xiàn)不同線程從單例實例中獲取不同的實體的情況呢?如下圖脱盲,我們通過分析去解釋邑滨,為何他是線程不安全的。

image

假設(shè)钱反,當前有兩個線程同時首次獲取此單例中的實例時:

  1. 線程一執(zhí)行g(shù)etInstence方法掖看,并判斷instance實例是否已經(jīng)被初始化匣距。
  2. 線程一判斷instance為null,執(zhí)行到 2 處哎壳,此時線程一還沒有開始執(zhí)行毅待,然后執(zhí)行權(quán)被線程二獲取,線程一進入等待归榕。
  3. 線程二執(zhí)行到 1 處判斷instance為null尸红,因為線程一即將開始初始化instance,但是還沒有初始化刹泄。
  4. 線程二執(zhí)行到 2 處開始初始化instence方法外里,并完成初始化,返回一個instance實例特石。
  5. 這時線程一被喚醒盅蝗,繼續(xù)從 2 處執(zhí)行,開始初始化instence方法姆蘸,并且也返回一個instance實例墩莫。

這樣,線程一和線程二從單例中獲取了兩個不同的實例逞敷。針對懶漢式的這種線程不安全的現(xiàn)象狂秦,攻城獅們也是開始頭腦風暴來改善它,比較容易想到的是將getInstence方法加鎖兰粉,來實現(xiàn)懶漢式的線程安全:

image

這樣雖然看似解決問題了故痊,但是未免太過于激進了,synchronized鎖住獲取實例的整個方法玖姑,因此在并發(fā)獲取單例實例的時候會有性能問題愕秫,并且線程安全問題的出現(xiàn)只是在第一次獲取實例的情況才會出現(xiàn),初始化之后不會再出現(xiàn)性能問題焰络,synchronized鎖的運用未免因小失大戴甩。

于是為了線程安全,還為了能在并發(fā)情況下高效的性能闪彼,便有了Double check(雙重檢索)的懶漢式單例

image

Double check的理論為:當?shù)谝淮蝿?chuàng)建單例實例的時候甜孤,只有一個線程可以去創(chuàng)建實例,因此不會出現(xiàn)多個線程獲取不同實例的情況畏腕。

假設(shè)時間序列:

  1. 線程一進入getInstence方法
  2. 線程一判斷instence為null缴川,并在 1 處進入synchronize塊,此時線程二開始執(zhí)行描馅,線程一等待
  3. 線程二進入getInstence方法把夸,判斷instence為null,并準備進入synchronize塊铭污,此時發(fā)現(xiàn)synchronize塊的鎖被占用恋日,因此進入等待
  4. 線程一開始再次判斷instence為null膀篮,然后開始初始化instence實例,然后釋放synchronize的鎖岂膳,獲取到了實例執(zhí)行完成
  5. 此時線程二開始得到synchronize鎖誓竿,進入synchronize塊再次判斷instence是否為null,發(fā)現(xiàn)instence此時已經(jīng)有值谈截,釋放鎖筷屡,直接獲取instence實例返回

Double check的理論看起來非常的完美,然而一切到頭來發(fā)現(xiàn)僅僅是想得美而已傻盟,在實際運行中他還是有問題的速蕊。

年輕稚嫩的猿也許會一臉懵逼嫂丙,老謀深算的猿也許會微微一笑娘赴,但是可能他們都會想 弄啥子嘞?

其實跟啤,這個理論的失敗诽表,并不是jvm實現(xiàn)的bug,而是歸咎于Java平臺的內(nèi)存模型隅肥,Java的內(nèi)存模型是圍繞著如何在并發(fā)過程中處理原子性竿奏、可見性、有序性這3個特征建立的腥放,而針對有序性泛啸,引用深入JVM虛擬機中的一句話解釋是:如果在本線程中觀察,所有的操作都是有序的秃症;如果在一個線程中觀察另一個線程候址,所有的操作都是無序的。前半句是指“線程內(nèi)表現(xiàn)為串行指令”种柑,后半句是指“指令重排序”現(xiàn)象和“工作內(nèi)存與主內(nèi)存同步延遲”現(xiàn)象岗仑。而針對原子性,看似簡單一行代碼聚请,經(jīng)過虛擬機編譯成字節(jié)碼信息后荠雕,可能就不是一行代碼了。而針對可見性驶赏,一個線程改變的變量值炸卑,并不會立刻對其他線程可見。

而上面Double check代碼失敗的源頭就是 instence = new DoubleCheck(); 這句話煤傍,而這句看似簡的一句話盖文,其實在虛擬機中分成了三個步驟:

  1. 為即將實例化的對象分配內(nèi)存空間
  2. 初始化單例實體對象執(zhí)行構(gòu)造函數(shù)
  3. 將內(nèi)存空間地址賦值給instence實例引用

也就是說其實我們所謂的new對象 并不是一個原子操作,并且患久,針對上面的2 3 步驟虛擬機會進行指令重排序椅寺,如果上面的Double check代碼的對象實例化的經(jīng)過重排序順序變成1 3 2 的話浑槽,就會出現(xiàn)問題:

  1. 線程一進入getInstence方法
  2. 線程一判斷instence為null,并在 1 處進入synchronize塊
  3. 線程一再次判斷instence為null返帕,最后執(zhí)行到 3 處桐玻,然而分配完內(nèi)存,獲取到實例地址荆萤,此時instence不再為null镊靴,但是還未初始化對象執(zhí)行構(gòu)造方法,此時縣城而獲取執(zhí)行權(quán)链韭,線程一被掛起
  4. 線程二獲取getInstence方法偏竟,并判斷instence不再null,然后獲取到了一個instence對象的地址敞峭,但是此時instence對象并未完成初始化踊谋,線程二后續(xù)執(zhí)行就會出現(xiàn)問題
  5. 線程一此時蘇醒,完成后面的instence對象初始化的動作旋讹,并返回實例

然而在jdk1.5以后殖蚕,這種情況有了解決方法,原因在于jdk1.5開始針對volatile進行了增強沉迹,volatile變量開始可以屏蔽指令重排睦疫,也就是說

當我們將instence引用進行volatile進行修飾的話instence = new DoubleCheck();這句話中的指令將不會被指令重排序,Double check也就不再只是想想了鞭呕。附上完整代碼:

image

三蛤育、靜態(tài)內(nèi)部類

image

靜態(tài)內(nèi)部類的優(yōu)點是:外部類加載時并不會立即加載內(nèi)部類,內(nèi)部類不被加載就不去初始化實例葫松,因此實現(xiàn)了懶加載瓦糕。當StaticSingle第一次被加載時,并不需要去加載內(nèi)部類Holder进宝,只有當getInstance()方法第一次被調(diào)用時刻坊,才會導(dǎo)致虛擬機加載Holer類菜會去初始化StaticSingle實例。這種方法不僅能確保線程安全党晋,也能保證單例的唯一性谭胚,同時也延遲了單例的實例化。

那么靜態(tài)內(nèi)部類是如何實現(xiàn)線程安全的呢未玻?我們需要了解下面一些只是

針對于類的初始化灾而,JVM虛擬機嚴格規(guī)定了有且僅有5種情況必須對類進行“初始化“:

  1. 遇到new、getstatic扳剿、setstatic或者invikestatic這4個字節(jié)碼指令時旁趟,對應(yīng)的java代碼場景為:new一個關(guān)鍵字或者一個實例化對象時、讀取或設(shè)置一個靜態(tài)字段時(final修飾庇绽、已在編譯期把結(jié)果放入常量池的除外)锡搜、調(diào)用一個類的靜態(tài)方法時橙困。
  2. 使用java.lang.reflect包的方法對類進行反射調(diào)用的時候,如果類沒進行初始化耕餐,需要先調(diào)用其初始化方法進行初始化凡傅。
  3. 當初始化一個類時,如果其父類還未進行初始化肠缔,會先觸發(fā)其父類的初始化夏跷。
  4. 當虛擬機啟動時,用戶需要指定一個要執(zhí)行的主類(包含main()方法的類)明未,虛擬機會先初始化這個類槽华。
  5. 當使用JDK 1.7等動態(tài)語言支持時,如果一個java.lang.invoke.MethodHandle實例最后的解析結(jié)果REF_getStatic趟妥、REF_putStatic猫态、REF_invokeStatic的方法句柄,并且這個方法句柄所對應(yīng)的類沒有進行過初始化煮纵,則需要先觸發(fā)其初始化懂鸵。

這5種情況被稱為是類的主動引用偏螺,注意行疏,這里《虛擬機規(guī)范》中使用的限定詞是"有且僅有",那么套像,除此之外的所有引用類都不會對類進行初始化酿联,稱為被動引用。靜態(tài)內(nèi)部類就屬于被動引用的行列夺巩。

我們再回頭看下getInstance()方法贞让,調(diào)用的是Holer.INSTANCE,取的是Holer里的INSTANCE對象柳譬,跟上面那個DCL方法不同的是喳张,getInstance()方法并沒有多次去new對象盯漂,故不管多少個線程去調(diào)用getInstance()方法棠笑,取的都是同一個INSTANCE對象,而不用去重新創(chuàng)建奥帘。當getInstance()方法被調(diào)用時制跟,Holer才在StaticSingle的運行時常量池里舅桩,把符號引用替換為直接引用,這時靜態(tài)對象INSTANCE也真正被創(chuàng)建雨膨,然后再被getInstance()方法返回出去擂涛,這點同餓漢模式。那么INSTANCE在創(chuàng)建過程中又是如何保證線程安全的呢聊记?在《深入理解JAVA虛擬機》中撒妈,有這么一句話:

虛擬機會保證一個類的()方法在多線程環(huán)境中被正確地加鎖恢暖、同步,如果多個線程同時去初始化一個類狰右,那么只會有一個線程去執(zhí)行這個類的()方法胀茵,其他線程都需要阻塞等待,直到活動線程執(zhí)行()方法完畢挟阻。如果在一個類的()方法中有耗時很長的操作琼娘,就可能造成多個進程阻塞(需要注意的是,其他線程雖然會被阻塞附鸽,但如果執(zhí)行()方法后脱拼,其他線程喚醒之后不會再次進入()方法。同一個加載器下坷备,一個類型只會初始化一次熄浓。),在實際應(yīng)用中省撑,這種阻塞往往是很隱蔽的赌蔑。

故而,可以看出INSTANCE在創(chuàng)建過程中是線程安全的竟秫,所以說靜態(tài)內(nèi)部類形式的單例可保證線程安全娃惯,也能保證單例的唯一性,同時也延遲了單例的實例化肥败。

那么趾浅,是不是可以說靜態(tài)內(nèi)部類單例就是最完美的單例模式了呢?其實不然馒稍,靜態(tài)內(nèi)部類也有著一個致命的缺點皿哨,就是傳參的問題,由于是靜態(tài)內(nèi)部類的形式去創(chuàng)建單例的纽谒,故外部無法傳遞參數(shù)進去证膨,例如Context這種參數(shù),所以鼓黔,我們創(chuàng)建單例時央勒,可以在靜態(tài)內(nèi)部類與DCL模式里自己斟酌。

四请祖、枚舉單例

從上述3種單例模式的寫法中订歪,似乎也解決了效率或者懶加載以及線程安全的問題,但是它們都有兩個共同的缺點:

  • 序列化可能會破壞單例模式肆捕,比較每次反序列化一個序列化的對象實例時都會創(chuàng)建一個新的實例刷晋,解決方案如下:
image
  • 使用反射強行調(diào)用私有構(gòu)造器,解決方式可以修改構(gòu)造器,讓它在創(chuàng)建第二個實例的時候拋異常眼虱,解決方案如下:
image

如上所述喻奥,問題確實也得到了解決,但問題是我們?yōu)榇烁冻隽瞬簧倥δ笮刺砑恿瞬簧俅a撞蚕,還應(yīng)該注意到如果單例類維持了其他對象的狀態(tài)時還需要使他們成為transient的對象,這種就更復(fù)雜了过牙,那有沒有更簡單更高效的呢甥厦?當然是有的,那就是枚舉單例了寇钉,先來看看如何實現(xiàn):

image

代碼相當簡潔刀疙,我們也可以像常規(guī)類一樣編寫enum類,為其添加變量和方法扫倡,訪問方式也更簡單谦秧,使用EnumSingle.INSTANCE進行訪問,這樣也就避免調(diào)用getInstance方法撵溃,更重要的是使用枚舉單例的寫法疚鲤,我們完全不用考慮序列化和反射的問題。枚舉序列化是由jvm保證的缘挑,每一個枚舉類型和定義的枚舉變量在JVM中都是唯一的集歇。

在枚舉類型的序列化和反序列化上,Java做了特殊的規(guī)定:在序列化時Java僅僅是將枚舉對象的name屬性輸出到結(jié)果中卖哎,反序列化的時候則是通過java.lang.Enum的valueOf方法來根據(jù)名字查找枚舉對象鬼悠。同時,編譯器是不允許任何對這種序列化機制的定制的并禁用了writeObject亏娜、readObject、readObjectNoData蹬挺、writeReplace和readResolve等方法维贺,從而保證了枚舉實例的唯一性,這里我們不妨再次看看Enum類的valueOf方法:

public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                              String name) {
      T result = enumType.enumConstantDirectory().get(name);
      if (result != null)
          return result;
      if (name == null)
          throw new NullPointerException("Name is null");
      throw new IllegalArgumentException(
          "No enum constant " + enumType.getCanonicalName() + "." + name);
  }

實際上通過調(diào)用enumType(Class對象的引用)的enumConstantDirectory方法獲取到的是一個Map集合巴帮,在該集合中存放了以枚舉name為key和以枚舉實例變量為value的Key&Value數(shù)據(jù)溯泣,因此通過name的值就可以獲取到枚舉實例,看看enumConstantDirectory方法源碼:

Map<String, T> enumConstantDirectory() {
        if (enumConstantDirectory == null) {
            //getEnumConstantsShared最終通過反射調(diào)用枚舉類的values方法
            T[] universe = getEnumConstantsShared();
            if (universe == null)
                throw new IllegalArgumentException(
                    getName() + " is not an enum type");
            Map<String, T> m = new HashMap<>(2 * universe.length);
            //map存放了當前enum類的所有枚舉實例變量榕茧,以name為key值
            for (T constant : universe)
                m.put(((Enum<?>)constant).name(), constant);
            enumConstantDirectory = m;
        }
        return enumConstantDirectory;
    }
    private volatile transient Map<String, T> enumConstantDirectory = null;

到這里我們也就可以看出枚舉序列化確實不會重新創(chuàng)建新實例垃沦,jvm保證了每個枚舉實例變量的唯一性。再來看看反射到底能不能創(chuàng)建枚舉用押,下面試圖通過反射獲取構(gòu)造器并創(chuàng)建枚舉

public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
   //獲取枚舉類的構(gòu)造函數(shù)(前面的源碼已分析過)
   Constructor<EnumSingle> constructor=EnumSingle.class.getDeclaredConstructor(String.class,int.class);
   constructor.setAccessible(true);
   //創(chuàng)建枚舉
   EnumSingle singleton=constructor.newInstance("otherInstance",9);
  }

執(zhí)行報錯

Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
    at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
    at zejian.SingletonEnum.main(SingletonEnum.java:38)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

顯然告訴我們不能使用反射創(chuàng)建枚舉類肢簿,這是為什么呢?不妨看看newInstance方法源碼:

 public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, null, modifiers);
            }
        }
        //這里判斷Modifier.ENUM是不是枚舉修飾符,如果是就拋異常
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);
        return inst;
    }

源碼很了然池充,確實無法使用反射創(chuàng)建枚舉實例桩引,也就是說明了創(chuàng)建枚舉實例只有編譯器能夠做到而已。顯然枚舉單例模式確實是很不錯的選擇收夸,因此我們推薦使用它坑匠。但是這總不是萬能的,對于android平臺這個可能未必是最好的選擇卧惜,在android開發(fā)中厘灼,內(nèi)存優(yōu)化是個大塊頭,而使用枚舉時占用的內(nèi)存常常是靜態(tài)變量的兩倍還多咽瓷,因此android官方在內(nèi)存優(yōu)化方面給出的建議是盡量避免在android中使用enum手幢。但是不管如何,關(guān)于單例忱详,我們總是應(yīng)該記孜Ю础:線程安全,延遲加載匈睁,序列化與反序列化安全监透,反射安全是很重重要的。

至此航唆,單例模式的介紹完畢胀蛮,不足之處大家補充指點。

參考:

https://blog.csdn.net/chenchaofuck1/article/details/51702129

https://blog.csdn.net/mnb65482/article/details/80458571

https://blog.csdn.net/javazejian/article/details/71333103#%E6%9E%9A%E4%B8%BE%E4%B8%8E%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F

《深入理解Java虛擬機 JVM高級特性與最佳實踐》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末糯钙,一起剝皮案震驚了整個濱河市粪狼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌任岸,老刑警劉巖再榄,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異享潜,居然都是意外死亡困鸥,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門剑按,熙熙樓的掌柜王于貴愁眉苦臉地迎上來疾就,“玉大人,你說我怎么就攤上這事艺蝴♀” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵猜敢,是天一觀的道長姑荷。 經(jīng)常有香客問我盒延,道長,這世上最難降的妖魔是什么厢拭? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任兰英,我火速辦了婚禮,結(jié)果婚禮上供鸠,老公的妹妹穿的比我還像新娘畦贸。我一直安慰自己,他們只是感情好楞捂,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布薄坏。 她就那樣靜靜地躺著,像睡著了一般寨闹。 火紅的嫁衣襯著肌膚如雪胶坠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天繁堡,我揣著相機與錄音沈善,去河邊找鬼。 笑死椭蹄,一個胖子當著我的面吹牛闻牡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播绳矩,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼罩润,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了翼馆?” 一聲冷哼從身側(cè)響起割以,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎应媚,沒想到半個月后严沥,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡珍特,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年祝峻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扎筒。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖酬姆,靈堂內(nèi)的尸體忽然破棺而出嗜桌,到底是詐尸還是另有隱情,我是刑警寧澤辞色,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布骨宠,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏层亿。R本人自食惡果不足惜桦卒,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望匿又。 院中可真熱鬧方灾,春花似錦、人聲如沸碌更。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽痛单。三九已至嘿棘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間旭绒,已是汗流浹背鸟妙。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留挥吵,地道東北人重父。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像蔫劣,于是被迫代替她去往敵國和親坪郭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 單例模式(SingletonPattern)一般被認為是最簡單脉幢、最易理解的設(shè)計模式歪沃,也因為它的簡潔易懂,是項目中最...
    成熱了閱讀 4,226評論 4 34
  • 前言 本文主要參考 那些年嫌松,我們一起寫過的“單例模式”沪曙。 何為單例模式? 顧名思義萎羔,單例模式就是保證一個類僅有一個...
    tandeneck閱讀 2,485評論 1 8
  • 1.單例模式概述 (1)引言 單例模式是應(yīng)用最廣的模式之一液走,也是23種設(shè)計模式中最基本的一個。本文旨在總結(jié)通過Ja...
    曹豐斌閱讀 2,864評論 6 47
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理贾陷,服務(wù)發(fā)現(xiàn)缘眶,斷路器,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • 哈哈哈哈哈哈哈
    找找找丟丟丟閱讀 154評論 0 0