在java中存在很多通用的設計模式,今天我簡單總結一下單例設計模式:
解決問題:解決一個類在內存中只存在一個對象的問題(比如說一個軟件中的配置文件)
一、如何保證對象的唯一性:
1、爲避免建立過多的該類對象,應首先禁止其他應用程序創建該類對象。
2、爲讓其他應用程序訪問到該對象,在本類中自定義一個對象,爲避免直接訪問該對象,要對其進行私有化。
3、提供訪問方式,便於其他程序對自定義對象的訪問,提供的訪問方法是公有的。
對象保證是惟一的了,那麼該如何具體實現呢?
二、使對象唯一性的步驟:
1、將構造函數私有化
2、在類中創建一個本類對象,並設置爲私有的
3、提供一個公有的方法獲取該對象,便於使用
三、單例設計模式的具體表現形式
具體代碼如下:
1、餓漢式:先初始化對象,類一進內存就加載
[java] view plaincopyprint?
- <span style="font-family:Arial;font-size:12px;">//餓漢式
- class Single
- {
- private Single(){}
- private static Single s = new Single();
- public static Single getSingle()
- {
- return s;
- }
- }
- class SingleText
- {
- public static void main(String [] args)
- {
- Single s1 = Single.getSingle();
- Single s2 = Single.getSingle();
- if (s1==s2)
- System.out.println(true);
- else
- System.out.println(false);
- }
- }</span>
2、懶漢式:類進內存,對象還沒有存在,只有調用了getSingle方法時,才建立對象
[java] view plaincopyprint?
- <span style="font-family:Arial;font-size:12px;">//懶漢式
- class Single
- {
- private Single(){}
- private static Single s = null;
- public static Single getSingle()
- {
- if (s==null)
- s = new Single();
- return s;
- }
- }
- class SingleText
- {
- public static void main(String [] args)
- {
- Single s1 = Single.getSingle();
- Single s2 = Single.getSingle();
- if (s1==s2)
- System.out.println(true);
- else
- System.out.println(false);
- }
- }</span>
運行的結果是:true;這是因爲s1和s2引用的是同一個對象,所以符合條件。
但是對於第二種懶漢式的單例設計模式,會出現一些小小的問題,當一個線程調用時,是沒什麼問題的,如果多個線程調用此種方式,那麼就會出現問題。
[java] view plaincopyprint?
- <span style="font-family:Arial;font-size:12px;">
- class Single
- {
- private static Single s = null;
- private Single(){}
- public static Single getInstance()
- {
- if (s == null)
- {
- synchronized(Single.class)
- {
- if (s == null)
- s = new Single();
- }
- }
- return s;
- }
- }</span>
比如說,當A調用時,當讀到if(s1==null)
時,可能就停在這了,然後cpu再調用B,B也讀到if(s1==null)這停下了,cpu再切換到A,接着創建一個對象,A就執行完了;之後B也向下執行,又創建一個對象;此時,對象就不唯一了,就破壞了對象的唯一性的初衷。那麼解決方案是這樣的:
這利用了鎖的機制。synchronized是java語言的關鍵字,當它用來修飾一個方法或者一個代碼塊的時候,能夠保證在同一時刻最多隻有一個線程執行該段代碼。這涉及到了多線程的問題。在這個例子中,比如說,當A調用時,當讀到第二個if(s1==null)
時,可能就停在這了,然後cpu再調用B,B讀到第一個if(s1==null)這停下了,因爲加上synchronized後,A進去就相當於將其他的調用鎖在外面的語句上了,要先執行完A,那麼A執行完後,就已經創建了一個對象;當B再讀到第二個if(s1==null)的時候不符合就直接結束了。如果再有其他C或D等調用的時候,就直接不符合第一個(s1==null)的條件,所以直接返回s。在這裏,我們再來看看關於懶漢式的多線程問題:
上面的懶漢式的寫法,是效率比較高的,先看看下面一段代碼,比較一下,就會清晰很多:
[java] view plaincopyprint?
- <span style="font-family:Arial;font-size:12px;">
- class Single
- {
- private static Single s = null;
- private Single(){}
- public static synchronized Single getInstance()
- {
- if (s == null)
- s = new Single();
- return s;
- }
- }</span>
在這兩種方式中,含有雙重判斷(稱爲第一種,無雙重判斷的爲第二種)的效率更高,爲什呢?雖然第一種和第二種都要先判斷一下,但是對於第一種,第一個線程執行完後,s不爲null了,那麼後面只需要判斷s是否爲null即可,而對於第二種,要先判斷鎖,鎖裏沒有線程,再進入,然後再判斷一下s是否爲null,這樣一來,就要判斷兩次,所以,效率會更低。所以,對於雙重判斷,是可以提高效率的。
問題是解決了,但是相比之下,還是第一種餓漢式的單例設計模式更好一些,是一種建議使用的方式。