單例設計模式

java中存在很多通用的設計模式,今天我簡單總結一下單例設計模式:

解決問題:解決一個類在內存中只存在一個對象的問題(比如說一個軟件中的配置文件)

一、如何保證對象的唯一性:

1、爲避免建立過多的該類對象,應首先禁止其他應用程序創建該類對象。

2、爲讓其他應用程序訪問到該對象,在本類中自定義一個對象,爲避免直接訪問該對象,要對其進行私有化。

3、提供訪問方式,便於其他程序對自定義對象的訪問,提供的訪問方法是公有的。

對象保證是惟一的了,那麼該如何具體實現呢?

二、使對象唯一性的步驟:

1、將構造函數私有化

2、在類中創建一個本類對象,並設置爲私有的

3、提供一個公有的方法獲取該對象,便於使用

三、單例設計模式的具體表現形式

具體代碼如下:

1、餓漢式:先初始化對象,類一進內存就加載

[java] view plaincopyprint?

  1. <span style="font-family:Arial;font-size:12px;">//餓漢式  
  2. class Single   
  3. {  
  4.     private Single(){}  
  5.     private static Single s = new Single();  
  6.     public static  Single getSingle()  
  7.     {  
  8.         return s;  
  9.     }  
  10. }  
  11.   
  12. class SingleText  
  13. {  
  14.     public static void main(String [] args)  
  15.     {  
  16.         Single s1 = Single.getSingle();  
  17.         Single s2 = Single.getSingle();  
  18.         if (s1==s2)  
  19.             System.out.println(true);  
  20.         else  
  21.             System.out.println(false);        
  22.     }  
  23. }</span>  

 

2、懶漢式:類進內存,對象還沒有存在,只有調用了getSingle方法時,才建立對象

[java] view plaincopyprint?

  1. <span style="font-family:Arial;font-size:12px;">//懶漢式  
  2. class Single   
  3. {  
  4.     private Single(){}  
  5.     private static Single s = null;  
  6.     public static Single getSingle()  
  7.     {  
  8.         if (s==null)   
  9.             s = new Single();  
  10.         return s;  
  11.     }  
  12. }  
  13.   
  14. class SingleText  
  15. {  
  16.     public static void main(String [] args)  
  17.     {  
  18.         Single s1 = Single.getSingle();  
  19.         Single s2 = Single.getSingle();  
  20.         if (s1==s2)  
  21.             System.out.println(true);  
  22.         else  
  23.             System.out.println(false);        
  24.     }  
  25. }</span>  

 

運行的結果是:true;這是因爲s1s2引用的是同一個對象,所以符合條件。


但是對於第二種懶漢式的單例設計模式,會出現一些小小的問題,當一個線程調用時,是沒什麼問題的,如果多個線程調用此種方式,那麼就會出現問題。

[java] view plaincopyprint?

  1. <span style="font-family:Arial;font-size:12px;">
  2.  
  3. class Single  
  4. {  
  5.     private static Single s = null;  
  6.     private Single(){}  
  7.     public static Single getInstance()  
  8.     {  
  9.         if (s == null)  
  10.         {  
  11.             synchronized(Single.class)  
  12.             {  
  13.                 if (s == null)  
  14.                     s = new Single();  
  15.             }  
  16.         }  
  17.         return s;  
  18.     }  
  19. }</span>  

 

比如說,當A調用時,當讀到if(s1==null) 時,可能就停在這了,然後cpu再調用BB也讀到if(s1==null)這停下了,cpu再切換到A,接着創建一個對象,A就執行完了;之後B也向下執行,又創建一個對象;此時,對象就不唯一了,就破壞了對象的唯一性的初衷。那麼解決方案是這樣的:
這利用了鎖的機制。synchronizedjava語言的關鍵字,當它用來修飾一個方法或者一個代碼塊的時候,能夠保證在同一時刻最多隻有一個線程執行該段代碼。這涉及到了多線程的問題。在這個例子中,比如說,A調用時,當讀到第二個if(s1==null) 時,可能就停在這了,然後cpu再調用BB讀到第一個if(s1==null)這停下了,因爲加上synchronized後,A進去就相當於將其他的調用鎖在外面的語句上了,要先執行完A,那麼A執行完後,就已經創建了一個對象;當B再讀到第二個if(s1==null)的時候不符合就直接結束了。如果再有其他CD等調用的時候,就直接不符合第一個(s1==null)的條件,所以直接返回s。在這裏,我們再來看看關於懶漢式的多線程問題:

上面的懶漢式的寫法,是效率比較高的,先看看下面一段代碼,比較一下,就會清晰很多:

[java] view plaincopyprint?

  1. <span style="font-family:Arial;font-size:12px;">
  2.  
  3. class Single  
  4. {  
  5.     private static Single s = null;  
  6.     private Single(){}  
  7.     public static synchronized Single getInstance()  
  8.     {  
  9.         if (s == null)  
  10.             s = new Single();  
  11.         return s;  
  12.     }  
  13. }</span>  


在這兩種方式中,含有雙重判斷(稱爲第一種,無雙重判斷的爲第二種)的效率更高,爲什呢?雖然第一種和第二種都要先判斷一下,但是對於第一種,第一個線程執行完後,s不爲null了,那麼後面只需要判斷s是否爲null即可,而對於第二種,要先判斷鎖,鎖裏沒有線程,再進入,然後再判斷一下s是否爲null,這樣一來,就要判斷兩次,所以,效率會更低。所以,對於雙重判斷,是可以提高效率的。

 

問題是解決了,但是相比之下,還是第一種餓漢式的單例設計模式更好一些,是一種建議使用的方式。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章