Java設(shè)計模式基礎(chǔ) - 單例模式

      網(wǎng)友投稿 883 2022-05-30

      單例模式是一種常見的設(shè)計模式,在這個模式下,單例對象的類必須保證只有一個實例存在,并提供返回實例對象的方法。在日常工作中,線程池、緩存、日志等對象通常被設(shè)計成單例模式,一方面減少了頻繁創(chuàng)建銷毀對象用以提升性能,另一方面避免了對共享資源的多重占用并簡化了訪問。

      那么在高并發(fā)、多線程的環(huán)境下,是如何確保多個線程操作的是同一對象,也就是說保證對象的唯一性呢?這時就要用到單例模式,來確保實例化過程中,對象只被實例化了一次。本文將介紹一下單例模式的幾種實現(xiàn)方式及性能分析。

      1.餓漢模式

      餓漢模式比較簡單,在實例初始化的時候不管有沒有用到,都會把實例先創(chuàng)建好,等待被調(diào)用。

      public class HungrySingleton { private static HungrySingleton instance=new HungrySingleton(); private HungrySingleton(){} //返回實例對象 public static HungrySingleton getInstance(){ return instance; } }

      由于在加載的時候已經(jīng)被實例化,只會創(chuàng)建一個實例,因此餓漢模式是線程安全的,能夠充分保證單例。但是沒有實現(xiàn)延遲加載,可能很長時間不被使用,影響程序性能。

      2.懶漢模式

      懶漢模式就是實例在被用到的時候才去創(chuàng)建,在使用的同時去檢查有沒有實例,如果有則返回,沒有則新建。

      public class HoonSingleton { private static HoonSingleton instance = null; public HoonSingleton() { } public HoonSingleton getInstance() { if (instance == null) { instance = new HoonSingleton(); } return instance; } }

      可以看出,在懶漢模式中,單例實例會被延遲加載,即只有在真正使用的時候才會實例化一個對象并交給自己的引用。由于使用了懶加載,因此在性能上要優(yōu)于餓漢模式。

      但是在多線程環(huán)境下,這種方法并不能夠保證實例對象的唯一性,多線程時可能多個線程同時去實例化對象,因此不能保證線程的安全性。在此基礎(chǔ)上進行改進,通過在getInstance()方法上加synchronized關(guān)鍵字,實現(xiàn)同步,可以實現(xiàn)線程安全。

      public synchronized static HoonSingleton getInstance() { if (instance == null) { instance = new HoonSingleton(); } return instance; }

      通過使用synchronized保證了對臨界資源的同步互斥訪問,也就保證了單例同步方法,這一方式實現(xiàn)了線程安全,但是相應(yīng)的該方法退化到了串行執(zhí)行,并且同步方法的作用域比較大,鎖的粒度太大,一定程度上降低了程序運行效率。

      3.DCL模式

      DCL模式又稱為雙檢鎖(Double Check Locking),也叫雙重校驗鎖,綜合了懶漢式和餓漢式兩者的優(yōu)點整合而成。

      public class DCL { private static DCL instance=null; private DCL(){ } public static DCL getInstance(){ if(null==instance) synchronized (DCL.class){ if(null==instance) instance=new DCL(); } return instance; } }

      DCL中,在synchronized關(guān)鍵字內(nèi)外都加了一層 if 條件判斷,這樣既保證了線程安全,又比直接上鎖提高了執(zhí)行效率,還節(jié)省了內(nèi)存空間。因此,在實現(xiàn)了懶加載與保證線程安全性的同時,也保證了較好的性能。

      盡管DCL看起來已經(jīng)非常完善了,但是由于存在JVM指令重排序的存在(不清楚的可以查看上一篇文章),使得DCL仍然存在一些問題。

      instance=new DCL();

      盡管是很簡單的一個語句,但是從執(zhí)行上來看,這并不是一個原子操作。這一語句大概完成了三件事情:

      給instance實例分配內(nèi)存

      使用instance的構(gòu)造方法實例對象

      將instance對象指向分配的內(nèi)存空間,必須注意,到此為止instance返回就已經(jīng)是非null的對象了

      在此情況下,JVM為了優(yōu)化指令提高程序運行效率,可能會將執(zhí)行順序中的第2、3步顛倒一下。以2個線程為例,可能出現(xiàn)以下情況:

      線程1,發(fā)現(xiàn)對象未實例化,準(zhǔn)備開始執(zhí)行構(gòu)造方法實例對象;

      線程2調(diào)用instance實例,發(fā)現(xiàn)對象已經(jīng)不為null,直接返回對象;

      對象構(gòu)造方法未執(zhí)行完畢,線程2調(diào)用instance中的一些對象返回空指針異常。

      根據(jù)以上分析可知,解決這個問題可以通過加volatile關(guān)鍵字來確定指令執(zhí)行順序,避免指令重排序

      private volatile static DCL instance=null;

      4.Holder模式

      Holder模式也被稱為靜態(tài)內(nèi)部類模式,在該模式下,可以通過使用內(nèi)部靜態(tài)類來以懶漢模式的思想來實現(xiàn)線程安全的對象單例。

      public class HolderDemo { private HolderDemo() {} private static class Holder { private static HolderDemo instance = new HolderDemo(); } public static HolderDemo getInstance() { return Holder.instance; } }

      可以看出,在聲明類的時候,它的成員中不包含需要聲明的實例變量,而放到它的內(nèi)部靜態(tài)類中去創(chuàng)建實例。而靜態(tài)的成員式內(nèi)部類,該內(nèi)部類的實例與外部類的實例沒有綁定關(guān)系,只有被調(diào)用到時才會裝載,這樣一來也實現(xiàn)了懶加載。

      5.枚舉方式

      枚舉實現(xiàn)方式是在Effective Java一書中被提到的,具有功能完善使用簡單,無償?shù)靥峁┝诵蛄谢瘷C制,在面對復(fù)雜的序列化或者反射攻擊時仍然可以絕對防止多次實例化等優(yōu)點。

      public class EnumSingletonDemo { private EnumSingletonDemo() { } private enum EnumHolder { INSTANCE; private EnumSingletonDemo instance; EnumHolder(){ instance = new EnumSingletonDemo(); } } public static EnumSingletonDemo getInstance() { return EnumHolder.INSTANCE.instance; } }

      由于Java中規(guī)定了每個枚舉類型及其定義的枚舉變量在JVM中都是唯一的,所以在加載的過程中只能被實例化一次,所以在其初始化的過程中是線程安全的。

      在序列化方面,Java中枚舉的序列化和反序列化都做了特殊的規(guī)定,這就可以避免反序列化過程中由于反射而導(dǎo)致的單例被破壞問題。使用枚舉的方式,能夠有效防止使用反射強行調(diào)用構(gòu)造方法創(chuàng)建實例。

      總結(jié)

      本文介紹了單例模式的主要思想,并列舉出了它的幾種經(jīng)典實現(xiàn),并對幾種實現(xiàn)的線程安全性與執(zhí)行效率進行了分析。總的來說,可以按照以下規(guī)則進行實現(xiàn)方式的選擇:

      Java設(shè)計模式基礎(chǔ) - 單例模式

      減少使用懶漢模式,線程安全或不安全模式下均有一定缺陷

      如果設(shè)計序列化與反序列化時,可以選擇枚舉的方式

      如果要實現(xiàn)懶加載,可以使用DCL及Holder模式

      未聲明需要懶加載,可以選擇餓漢模式

      最后

      覺得對您有所幫助,小伙伴們可以點個贊啊,非常感謝~

      公眾號『碼農(nóng)參上』,一個熱愛分享的公眾號,有趣、深入、直接,與你聊聊技術(shù)。歡迎來加Hydra好友 (- DrHydra9),圍觀朋友圈,做個之交啊。

      Java 多線程

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:Jetty9部署多個項目及虛擬主機配置的方法
      下一篇:【伙伴故事—青軟】僅用3年!躍升華為云教育類目伙伴TOP2
      相關(guān)文章
      国产av天堂亚洲国产av天堂 | 亚洲色无码一区二区三区| 亚洲大码熟女在线观看| 中文字幕乱码亚洲精品一区| 亚洲人成77777在线观看网| 亚洲黄色免费在线观看| 亚洲男人天堂av| 亚洲毛片在线观看| 99ri精品国产亚洲| 91亚洲国产成人精品下载| 亚洲精品动漫在线| 亚洲另类精品xxxx人妖| 亚洲喷奶水中文字幕电影| 亚洲人成影院77777| 亚洲综合丁香婷婷六月香| 亚洲一区二区三区在线网站 | 亚洲中文字幕无码久久精品1| 国产亚洲情侣一区二区无| 中文字幕在线亚洲精品| 亚洲色偷拍另类无码专区| 亚洲va中文字幕无码久久不卡| 亚洲av中文无码乱人伦在线播放 | 亚洲国产精品无码观看久久| 亚洲Av永久无码精品一区二区| 色偷偷亚洲第一综合网| 亚洲成a人无码av波多野按摩| 亚洲?v无码国产在丝袜线观看| 亚洲福利中文字幕在线网址| 亚洲精品成人片在线观看| 伊人久久综在合线亚洲91| 亚洲成av人片在线观看无码不卡| 久久亚洲AV午夜福利精品一区| 亚洲国产精品lv| 亚洲成综合人影院在院播放| 色老板亚洲视频免在线观| 亚洲色大成网站www尤物| 激情小说亚洲色图| 最新国产AV无码专区亚洲| 亚洲视频在线播放| 国内精品久久久久影院亚洲| 亚洲精华国产精华精华液 |