java設計模式之單例模式(七種方法)

單例模式:個人認為這個是最簡單的一種設計模式,而且也是在我們開發中最常用的一個設計模式。

單例模式的意思就是只有一個實例。單例模式確保某一個類只有一個實例,而且自行實例化并向整個系統提供這個實例。這個類稱為單例類。我們前面學習的很多類都是單例的。比如最典型的就是Servlet類!Servlet類被設計成單例,被所有線程共享!

Java Singleton模式為我們提供了這樣實現的可能。使用Singleton的好處還在于可以節省內存,因為它限制了實例的個數,有利于Java垃圾回收(garbage collection)。

?單例模式也是一種比較常見的設計模式,它到底能帶給我們什么好處呢?其實無非是三個方面的作用:

??????? ?1、控制資源的使用,通過線程同步來控制資源的并發訪問;

???? 2、控制實例產生的數量,達到節約資源的目的。

?????3、作為通信媒介使用,也就是數據共享,它可以在不建立直接關聯的條件下,讓多個不相關的兩個線程或者進程之間實現通信。

?

緩存、日志、創建比較耗時的往往可以設計成單例

?

單例模式主要點

  1、私有的靜態屬性

  2、私有的構造方法

  3、公共的創建接口

?

第一種(懶漢,線程不安全):

?

Java代碼??
  1. public?class?Singleton?{??
  2. ????private?static?Singleton?instance;??
  3. ????private?Singleton?(){}??
  4. ??
  5. ????public?static?Singleton?getInstance()?{??
  6. ????if?(instance?==?null)?{??
  7. ????????instance?=?new?Singleton();??
  8. ????}??
  9. ????return?instance;??
  10. ????}??
  11. }??

?

?這種寫法lazy loading很明顯,但是致命的是在多線程不能正常工作。

第二種(懶漢,線程安全):

?

Java代碼??
  1. public?class?Singleton?{??
  2. ????private?static?Singleton?instance;??
  3. ????private?Singleton?(){}??
  4. ????public?static?synchronized?Singleton?getInstance()?{??
  5. ????if?(instance?==?null)?{??
  6. ????????instance?=?new?Singleton();??
  7. ????}??
  8. ????return?instance;??
  9. ????}??
  10. }??

?

?這種寫法能夠在多線程中很好的工作,而且看起來它也具備很好的lazy loading,但是,遺憾的是,效率很低,99%情況下不需要同步。

第三種(餓漢):

?

Java代碼??
  1. public?class?Singleton?{??
  2. ????private?static?Singleton?instance?=?new?Singleton();??
  3. ????private?Singleton?(){}??
  4. ????public?static?Singleton?getInstance()?{??
  5. ????return?instance;??
  6. ????}??
  7. }??

?

?這種方式基于classloder機制避免了多線程的同步問題,不過,instance在類裝載時就實例化,雖然導致類裝載的原因有很多種,在單例模式中大多數都是調用getInstance方法,?但是也不能確定有其他的方式(或者其他的靜態方法)導致類裝載,這時候初始化instance顯然沒有達到lazy loading的效果。

第四種(漢,變種):

?

Java代碼??
  1. public?class?Singleton?{??
  2. ????private?Singleton?instance?=?null;??
  3. ????static?{??
  4. ????instance?=?new?Singleton();??
  5. ????}??
  6. ????private?Singleton?(){}??
  7. ????public?static?Singleton?getInstance()?{??
  8. ????return?this.instance;??
  9. ????}??
  10. }??

?

?表面上看起來差別挺大,其實更第三種方式差不多,都是在類初始化即實例化instance。

第五種(靜態內部類):

?

Java代碼??
  1. public?class?Singleton?{??
  2. ????private?static?class?SingletonHolder?{??
  3. ????private?static?final?Singleton?INSTANCE?=?new?Singleton();??
  4. ????}??
  5. ????private?Singleton?(){}??
  6. ????public?static?final?Singleton?getInstance()?{??
  7. ????return?SingletonHolder.INSTANCE;??
  8. ????}??
  9. }??

?

這種方式同樣利用了classloder的機制來保證初始化instance時只有一個線程,它跟第三種和第四種方式不同的是(很細微的差別):第三種和第四種方式是只要Singleton類被裝載了,那么instance就會被實例化(沒有達到lazy loading效果),而這種方式是Singleton類被裝載了,instance不一定被初始化。因為SingletonHolder類沒有被主動使用,只有顯示通過調用getInstance方法時,才會顯示裝載SingletonHolder類,從而實例化instance。想象一下,如果實例化instance很消耗資源,我想讓他延遲加載,另外一方面,我不希望在Singleton類加載時就實例化,因為我不能確保Singleton類還可能在其他的地方被主動使用從而被加載,那么這個時候實例化instance顯然是不合適的。這個時候,這種方式相比第三和第四種方式就顯得很合理。

第六種(枚舉):

?

Java代碼??
  1. public?enum?Singleton?{??
  2. ????INSTANCE;??
  3. ????public?void?whateverMethod()?{??
  4. ????}??
  5. }??

?

?這種方式是Effective Java作者Josh Bloch 提倡的方式,它不僅能避免多線程同步問題,而且還能防止反序列化重新創建新的對象,可謂是很堅強的壁壘啊,不過,個人認為由于1.5中才加入enum特性,用這種方式寫不免讓人感覺生疏,在實際工作中,我也很少看見有人這么寫過。

第七種(雙重校驗鎖):

?

Java代碼??
  1. public?class?Singleton?{??
  2. ????private?volatile?static?Singleton?singleton;??
  3. ????private?Singleton?(){}??
  4. ????public?static?Singleton?getSingleton()?{??
  5. ????if?(singleton?==?null)?{??
  6. ????????synchronized?(Singleton.class)?{??
  7. ????????if?(singleton?==?null)?{??
  8. ????????????singleton?=?new?Singleton();??
  9. ????????}??
  10. ????????}??
  11. ????}??
  12. ????return?singleton;??
  13. ????}??
  14. }??

?

?這個是第二種方式的升級版,俗稱雙重檢查鎖定,詳細介紹請查看:http://www.ibm.com/developerworks/cn/java/j-dcl.html

在JDK1.5之后,雙重檢查鎖定才能夠正常達到單例效果。

?

總結

有兩個問題需要注意:

1.如果單例由不同的類裝載器裝入,那便有可能存在多個單例類的實例。假定不是遠端存取,例如一些servlet容器對每個servlet使用完全不同的類裝載器,這樣的話如果有兩個servlet訪問一個單例類,它們就都會有各自的實例。

2.如果Singleton實現了java.io.Serializable接口,那么這個類的實例就可能被序列化和復原。不管怎樣,如果你序列化一個單例類的對象,接下來復原多個那個對象,那你就會有多個單例類的實例。

對第一個問題修復的辦法是:

?

Java代碼??
  1. private?static?Class?getClass(String?classname)??????
  2. ?????????????????????????????????????????throws?ClassNotFoundException?{?????
  3. ??????ClassLoader?classLoader?=?Thread.currentThread().getContextClassLoader();?????
  4. ??????
  5. ??????if(classLoader?==?null)?????
  6. ?????????classLoader?=?Singleton.class.getClassLoader();?????
  7. ??????
  8. ??????return?(classLoader.loadClass(classname));?????
  9. ???}?????
  10. }??

?對第二個問題修復的辦法是:

?

Java代碼??
  1. public?class?Singleton?implements?java.io.Serializable?{?????
  2. ???public?static?Singleton?INSTANCE?=?new?Singleton();?????
  3. ??????
  4. ???protected?Singleton()?{?????
  5. ????????
  6. ???}?????
  7. ???private?Object?readResolve()?{?????
  8. ????????????return?INSTANCE;?????
  9. ??????}????
  10. }???

?

對我來說,我比較喜歡第三種和第五種方式,簡單易懂,而且在JVM層實現了線程安全(如果不是多個類加載器環境),一般的情況下,我會使用第三種方式,只有在要明確實現lazy loading效果時才會使用第五種方式,另外,如果涉及到反序列化創建對象時我會試著使用枚舉的方式來實現單例,不過,我一直會保證我的程序是線程安全的,而且我永遠不會使用第一種和第二種方式,如果有其他特殊的需求,我可能會使用第七種方式,畢竟,JDK1.5已經沒有雙重檢查鎖定的問題了。

更多技術文章請到作者網站 java168信息網》》》

轉載于:https://www.cnblogs.com/quchengfeng/p/4111896.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/273509.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/273509.shtml
英文地址,請注明出處:http://en.pswp.cn/news/273509.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java 遍歷map集合

Map<String, String> map new HashMap<String, String>(); map.put("key1", "value1"); map.put("key2", "value2"); map.put("key3", "value3"); //第一種&#xff1a;通過Map.keySet遍…

poj3009 Curling 2.0 深搜

PS&#xff1a;以前看到題目這么長就沒寫下去了。今天做了半天&#xff0c;沒做出來。準備看題解&#xff0c;打開了網站都忍住了&#xff0c;最后還是靠自己做出來的。算是一點進步吧。 分析&#xff1a; 題目的意思沒明白或者理解有偏差都沒辦法做題。看樣例3和樣例4&#xf…

Android監聽事件

ListView事件監聽&#xff1a; setOnItemSelectedListener 鼠標滾動時觸發 setOnItemClickListener 點擊時觸發 EditText事件監聽&#xff1a; setOnKeyListener 獲取焦點時觸發 RadioGroup事件監聽&#xff1a; setOnCheckedChangeListener 點擊時觸發 CheckBox事件監聽&#…

子類能不能繼承父類的構造方法?

class A{ public A(){} // 1:無參數構造方法。 public A(String s){} // 2.}class B extends A{ public B(String s){ super(s); // 3. }}說明&#xff1a;如果沒有1處的無參數構造方法&#xff0c;那么3處一定要主動調用父類帶參數的構造方法。如果有1處的構造方法&#…

基于原生javascript的ajax實現

function getXMLHttpRequest(){if(window.ActiveXObject){//用戶是ie瀏覽器http_requestnew ActiveXObject("Microsoft.XMLHTTP");}else{//其他的瀏覽器http_requestnew XMLHttpRequest();}return http_request;}var httpRequest;function name(){httpRequestgetXMLH…

Google File System設計方面的問題匯總

1、Google File System概述 google file system是一個分布式文件系統&#xff0c;針對的是數據密集型應用&#xff0c;提供容錯功能&#xff0c;運行在低廉的服務器上&#xff0c;同時給大量的用戶提供高性能服務。盡管google file system有著傳統的分布式文件系統的目標&#…

linux phpize

phpize是什么 1、phpize是用來擴展php擴展模塊的&#xff0c;通過phpize可以建立php的外掛模塊。 當php編譯完后&#xff0c;在bin下面會有phpize這個腳本文件&#xff0c; 在編譯你要添加的擴展模塊之前&#xff0c;執行以下phpize就可以了&#xff1b; 比如現在想在php中加入…

一些常用的正則表達式

較驗郵箱&#xff1a; var EmailReg /^[-_A-Za-z0-9]([_A-Za-z0-9]\.)[A-Za-z0-9]{2,3}$/; 身份證號碼&#xff1a; var reg /(^\d{15}$)|(^\d{17}(\d|X)$)/; 15位身份證號 //身份證15位時&#xff0c;次序為省&#xff08;3位&#xff09;市&#xff08;3位&#xff…

iOS iphone屏幕分析(豈止而大)

在寫本文前&#xff0c;我必須介紹幾點內容&#xff1a;第一點&#xff1a;屏幕上面顯示的內容多少和屏幕的尺寸大小無關第二點&#xff1a;屏幕上面顯示的內容多少和分辨率完全無關第三點&#xff1a;屏幕上面顯示的內容多少和屏幕尺寸、屏幕分辨率、PPI等都是無關的那到底什么…

js的一些實現

響應回車鍵提交表單 //*******************************************************響應回車鍵登錄****************************************************************** document.οnkeydοwnfunction(event){ var e event || window.event || arguments…

【隨筆】Win7下GVIM的安裝與配置

針對各種語言的編輯器千千萬萬&#xff0c;最好的就是最適合自己的&#xff0c;這句話一點沒錯。 偶然間&#xff0c;需要在Windows上編寫代碼&#xff0c;MyEclipse等太大&#xff0c;完全沒有必要&#xff0c;所以就想起來了vim這個神器。個子小&#xff0c;功能強&#xff0…

java遍歷Set集合

在Java中使用Set,可以方便地將需要的類型&#xff0c;以集合類型保存在一個變量中.主要應用在顯示列表. Set是一個不包含重復元素的collection。更確切地講&#xff0c;set 不包含滿足 e1.equals(e2) 的元素對 e1 和 e2&#xff0c;并且最多包含一個 null 元素。 import java.u…

Java switch語句

在Java7之前&#xff0c;switch只能支持 byte、short、char、int或者其對應的封裝類以及Enum類型。 Java7可以使用String作為判斷條件 public class Test { public void test(String str) { switch(str) { case "abc": …

find之exec和args

本來以為以前的差不多夠用了。呵呵&#xff0c;看到很多高手用高技巧&#xff0c;心癢癢的覺得我自己還可以提升啊。。哈哈哈。 這個實踐起來之后&#xff0c;&#xff0c;SED,AWK也得深化一下&#xff0c;&#xff0c;&#xff0c;SHELL和PYTHON&#xff0c;作運維的兩樣都不能…

Java 字符串分割陷阱

Java中關于字符串有一個split方法&#xff0c;這個方法可以實現分割字符串的作用&#xff1b; 但是如果使用一些正則表達式中出現的字符時Java編譯器會報錯&#xff0c; 如&#xff1a; String str "com.zhangsan.lisi.wangwu"; String[] strArray str.split(…

Linux 復習重點目錄

Linux安全復習 一、Linux基本命令 1、文件管理命令 lvm 2、用戶管理命令 3、網絡管理命令 4、權限管理 普通權限和特殊權限 權限命令修改 5、服務命令 6、軟件安裝管理命令 yum安裝 prm包安裝 源碼包安裝 7、vim 、cat 、more、less文件處理 8、進程管理 top、ps、計劃任務、守…

java Math 方法

Math.round(12.49)12; Math.round(12.50)13; Math.round(0.5)1; Math.round(0.49)0; Math.round(-0.51)-1; Math.round(-0.5)0; Math.floor(-0.50)-1.0; Math.floor(-0.001)-1.0; Math.floor(12.50)12.0; Math.floor(12.99)12.0;

LeetCode First Missing Positive

Given an unsorted integer array, find the first missing positive integer. For example,Given [1,2,0] return 3,and [3,4,-1,1] return 2. Your algorithm should run in O(n) time and uses constant space. 解題思路&#xff1a;數組總共有n個數&#xff0c;若都是連續的…

[java] 虛擬機(JVM)底層結構詳解[轉]

[java] 虛擬機(JVM)底層結構詳解[轉] 本文來自&#xff1a;曹勝歡博客專欄。轉載請注明出處&#xff1a;http://blog.csdn.net/csh624366188 在以前的博客里面&#xff0c;我們介紹了在java領域中大部分的知識點&#xff0c;從最基礎的java最基本語法到SSH框架。這里面應該包含…

jquery擴張函數

//jquery擴展函數判斷是否是手機號碼 $.fn.isMobile function(){ alert("zhangsan"); var tmptxt$(this).val().trim(); var RegEx/^1[3|4|5|8][0-9]\d{8}$/;return RegEx.test(tmptxt); }; //jquery擴展函數判斷是否是固定電話 $.fn.isTel function()…