1. 程式人生 > >單例模式(1/23)

單例模式(1/23)

這樣理解應該過更好一點:

自從秦始皇確立了皇帝這個位置以後,同一時期基本上就只有一個人孤零零地坐在這個
位置。這種情況下臣民們也好處理,大家叩拜、談論的時候只要提及皇帝,每個人都知道指
的是誰,而不用在皇帝前面加上特定的稱呼,如張皇帝、李皇帝。這一個過程反應到設計領
域就是,要求一個類只能生成一個物件(皇帝),所有物件對它的依賴都是相同的,因為只
有一個物件,大家對它的脾氣和習性都非常瞭解,建立健壯穩固的關係,我們把皇帝這種特
殊職業通過程式來實現。
皇帝每天要上朝接待臣子、處理政務,臣子每天要叩拜皇帝,皇帝只能有一個,也就是
一個類只能產生一個物件,該怎麼實現呢?物件產生是通過new關鍵字完成的(當然也有其
他方式,比如物件複製、反射等),這個怎麼控制呀,但是大家別忘記了建構函式,使用
new關鍵字建立物件時,都會根據輸入的引數呼叫相應的建構函式,如果我們把建構函式設
置為private私有訪問許可權不就可以禁止外部建立物件了嗎?臣子叩拜唯一皇帝的過程類圖如

首先,在高併發情況下,請注意單例模式的執行緒同步問題。單例模式有幾種不同的實現
方式,上面的例子不會出現產生多個例項的情況,但是如程式碼清單7-4所示的單例模式就需
要考慮執行緒同步。
程式碼清單7-4 執行緒不安全的單例
public class Singleton {
private static Singleton singleton = null;
//限制產生多個物件
private Singleton(){
}
//通過該方法獲得例項物件
public static Singleton getSingleton(){
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
該單例模式在低併發的情況下尚不會出現問題,若系統壓力增大,併發量增加時則可能
在記憶體中出現多個例項,破壞了最初的預期。為什麼會出現這種情況呢?如一個執行緒A執行
到singleton = new Singleton(),但還沒有獲得物件(物件初始化是需要時間的),第二個執行緒
B也在執行,執行到(singleton == null)判斷,那麼執行緒B獲得判斷條件也是為真,於是繼續
執行下去,執行緒A獲得了一個物件,執行緒B也獲得了一個物件,在記憶體中就出現兩個物件!
解決執行緒不安全的方法很有多,可以在getSingleton方法前加synchronized關鍵字,也可以
在getSingleton方法內增加synchronized來實現,但都不是最優秀的單例模式,建議讀者使用如
程式碼清單7-3所示的方式(有的書上把程式碼清單7-3中的單例稱為餓漢式單例,在程式碼清單7-4
中增加了synchronized的單例稱為懶漢式單例)。
其次,需要考慮物件的複製情況。在Java中,物件預設是不可以被複制的,若實現了
Cloneable介面,並實現了clone方法,則可以直接通過物件複製方式建立一個新物件,物件
複製是不用呼叫類的建構函式,因此即使是私有的建構函式,物件仍然可以被複制。在一般
情況下,類複製的情況不需要考慮,很少會出現一個單例類會主動要求被複制的情況,解決
該問題的最好方法就是單例類不要實現Cloneable介面。

 

當皇帝不止一個的時候:

package SingleP;

import java.util.ArrayList;
import java.util.Random;

public class Emperor {
    public static int Max = 2;
    private static ArrayList<String> nameList = new ArrayList<String>();
    private static ArrayList<Emperor> empList = new ArrayList<Emperor>();
    private static int countNum = 0;
    static {
        for(int i=0;i<Max;i++){
            empList.add(new Emperor("king"+(i+1)));
        }
    }
    private Emperor(){
    }
    private Emperor(String empName){
        nameList.add(empName);
    }
    public static Emperor getInstance(){
        Random random = new Random();
        countNum = random.nextInt(Max);
        return empList.get(countNum);
    }
    public static void say(){
        System.out.println(nameList.get(countNum));
    }
}
package SingleP;

public class MinisterD {
    public static void main(String[] args){
        int minister = 5;
        for(int i =0;i<minister;i++){
            Emperor emperor = Emperor.getInstance();
            System.out.println(i+"pin visit");
            emperor.say();
        }
    }
}