單例模式

單例模式應該算是我們經常遇到的一種模式,比如說線程池就是應用了單例,Spring bean也是運用了單例,單例模式節省了整個系統內存的開銷。下面代碼基於線程安全的前提下。
1、餓漢式。類加載時就生成了相應的對象。

public class Singleton1 {

    private static Singleton1 instance = new Singleton1();

    private Singleton1(){}

    public static Singleton1 getInstance(){
        return instance;
    }

}

2、懶漢式。在第一次調用方法時才生成對象。

public class Singleton2 {

    private static Singleton2 instance;

    private Singleton2(){}

    public static synchronized Singleton2 getInstance(){
        if(instance == null){
            instance = new Singleton2();
        }
        return instance;
    }

}

3、雙重檢查式。兩次判斷,將同步放到方法內部,但是由於java內存模型,這個可能會出現問題。

public class Singleton3 {

    private static Singleton3 instance;

    private Singleton3(){}

    public static Singleton3 getInstance(){
        if(instance == null){
            synchronized (Singleton3.class){
                if(instance == null){
                    instance = new Singleton3();
                }
            }
        }
        return instance;
    }

}

4、靜態內部類。延遲加載,天然的線程安全。

public class Singleton4 {

    private static final class InstanceClass{
        private static Singleton4 instance = new Singleton4();
    }

    private Singleton4(){
        if(InstanceClass.instance != null){
            throw new RuntimeException();
        }
    }

    public static Singleton4 getInstance(){
        return InstanceClass.instance;
    }

}

5、枚舉式。jvm保證其線程安全性,效率也高。

public enum Singleton5 {

    INSTANCE;
    public void getInstance(){}

}

上面一共是五種單例的形式,每一種都有其對應的特點。總的來說,如果想要延遲加載,最好選用靜態內部類,如果不需要,枚舉較好。下面是對這五種模式創建對象效率的一個模擬測試,分別調用幾種模式每次生成10萬個對象。這裏採用了countDownLatch類作爲多線程環境下的計數器,CountDownLatch是一個同步工具類,它允許一個或多個線程一直等待,直到其他線程的操作執行完後再執行。

public class SingleTest {

    public static void main(String[] args) throws Exception {
        Long startTime = System.currentTimeMillis();
        int ThreadNum = 10;
        final CountDownLatch countDownLatch = new CountDownLatch(ThreadNum);
        for (int i = 0; i < ThreadNum; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000000; j++) {
                    Singleton1 s = Singleton1.getInstance();
                    //Singleton5 s = Singleton5.INSTANCE;
                }
                countDownLatch.countDown();
            }).start();

        }
        countDownLatch.await();
        Long endTime  =System.currentTimeMillis();
        System.out.println("耗時爲:" + (endTime - startTime));
    }

}

最後運行發現,除了懶漢式生成效率較低之外,其他幾種模式差距都很小。

發佈了56 篇原創文章 · 獲贊 23 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章