1. 程式人生 > >我是皇帝我獨苗之單例模式

我是皇帝我獨苗之單例模式

tro [] print 解決辦法 cto ini random 分析 day

什麽是單例模式?
?
確保某一個類只有一個實例,並且自行實例化並向整個系統提供這個實例。通俗一點就是確保一個類只能產生一個對象,所有對象對他的依賴都是相同的。
?
單例模式的幾種實現方式
?

  • 懶漢式(線程不安全)
    ?

    public class Singleton {  
        private static Singleton singleton;  
    
        private Singleton (){}  
        public static synchronized Singleton getInstance() {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        return singleton;  
        }  
    }
  • 餓漢式(線程安全)
    ?
    public class Singleton {  
    private static Singleton singleton = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return singleton;  
    }  
    }

單例模式的註意事項
?
高並發的情況下註意單例模式的線程同步問題,測試代碼如下:
?

public class Emperor {
    private static String name = null;
    private static Emperor emperor = null;
    //限制產生多個對象
    private Emperor(String e) {
        Emperor.name=e;
    }
    //通過該方法獲得實例對象
    public static Emperor getInstance(String e) {
        if (emperor == null) {
            emperor = new Emperor(e);
        }
        return emperor;
    }
    //類中的其他方法盡量使用static
    public static void say(String m) {
//      Emperor.name=e;
        System.out.println("我是:"+ m +", 參見皇帝"+Emperor.name);
    }
}

?

package com.zgz.dm.Singleton;

public class Minister {

    public static void main(String[] args) {
        Minister1 minister1 = new Minister1();
        Minister2 minister2 = new Minister2();

        new Thread(minister1).start();
        new Thread(minister2).start();

        /*for(int day=0; day<3; day++) {
            Emperor emperor = Emperor.getInstance();
            emperor.say();
        }*/
    }

}

class Minister1 implements Runnable{
    private String minister = "王安石";
    private String s = "宋神宗1";

    @Override
    public void run() {
        for(int i=0; i<10; i++) {
            Emperor emperor = Emperor.getInstance();
            emperor.say(s,minister);

            try {
                Thread.sleep((int)Math.random()*200);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

class Minister2 implements Runnable{
    private String minister = "蘇軾";
    private String s = "宋神宗2";

    @Override
    public void run() {
        for(int i=0; i<10; i++) {
            Emperor emperor = Emperor.getInstance();
            emperor.say(s,minister);

            try {
                Thread.sleep((int)Math.random()*1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

?
測試結果:
?
技術分享圖片
?
原因分析:
?
王安石應該是宋神宗1啊?為啥是宋神宗12呢,就說明內存中存在兩個對象。一個線程A執行到 emperor = new Emperor(),但還沒有獲得對象(對象的初始化需要時間),第二個線程B正在執行,執行到 (emperor == null),線程B獲得判斷條件為真,於是繼續執行。於是A與B都獲得了對象,內存中出現了兩個對象。
?
解決辦法:
?
加鎖,執行效率低。建議使用餓漢式單例

我是皇帝我獨苗之單例模式