設計模式學習--Singleton

What

Singleton:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。java

Why

Singletion是我比較熟悉的設計模式之一,在日常的開發過程當中,也曾幾回用到,它主要適用於以下場景:
一、當類只能有一個實例並且客戶能夠從一個衆所周知的訪問點訪問它時。
二、當這個惟一實例應該是經過子類可擴展的,而且客戶應該無需更改代碼就能使用一個擴展的實例時。
在系統設計中,在涉及系統資源的管理時,每每會被設計成Singletion模式,好比緩存、日誌對象、線程池、對話框等等。設計模式

How

假設以下場景:一個簡單的線程池,須要實現增長線程以及獲取單個線程的功能。顯然這個線程池對象是一個Singletion對象。
簡單的實現代碼以下:緩存

public class ThreadPool {

    private List<Runnable> threads=new ArrayList<Runnable>();

    private static ThreadPool threadPool=null;

    private ThreadPool(){

    }

    public static ThreadPool getInstance(){
        if(threadPool==null){
            threadPool=new ThreadPool();
        }
        return threadPool;
    }

    public void add(Runnable thread){
        System.out.append("add a thread!");
        threads.add(thread);
    }
}

客戶端調用安全

ThreadPool threadPool=ThreadPool.getInstance();
        threadPool.add(new Thread());

以上代碼類圖以下:

多線程

Discuss

線程安全的Singleton實現

以上代碼,實現的是一個學習意義上的版本,在實際生產中,在一些狀況下會出現問題,在多線程狀況下,以下代碼會出現什麼問題?app

public static ThreadPool getInstance(){
        if(threadPool==null){
            threadPool=new ThreadPool();
        }
        return threadPool;
    }

在多線程條件下,當一個線程執行到new ThreadPool()可是還沒返回給threadPool,這時thread=null,另外一個線程也會進入if代碼片斷,這樣就建立了兩個threadPool,形成這個的緣由就是這段代碼是線程非安全的。
通過優化造成以下版本:性能

public static ThreadPool getInstance() {

        synchronized (ThreadPool.class) {
            if (threadPool == null) {
                threadPool = new ThreadPool();
            }
        }
        return threadPool;
    }

這個版本能夠很好的解決上個版本的問題,在一個線程進入了synchronized代碼塊,另外一個線程就會等待。可是仔細想一想,若是對象已經建立,線程仍是須要等待進入synchronized代碼塊纔會知道threadPool!=null,這樣會形成比較嚴重性能問題,再來一個版本學習

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

ok,這個版本看上去完美了,能夠在生產中使用了。這是線程安全的實現。
以上代碼仍是能夠經過一些辦法在一個JVM中建立多個ThreadPool實例,想一想是什麼?對,能夠經過反射的方式來,建立n多個實例,java的反射機制能夠經過private的構造器建立實例。優化

使用枚舉實現Singleton

Effectvie java的做者Joshua Bloch提出了一個能夠絕對防止屢次實例化,並且無償的提供了序列化機制的方法,使用枚舉實現Singleton,固然java版本須要在1.5以上,下面是以上示例的使用枚舉的實現spa

public enum ThreadPool {

    Instance;

    private List<Runnable> threads=new ArrayList<Runnable>();

    public void add(Runnable thread){
        System.out.append("add a thread!");
        threads.add(thread);
    }
}

客戶端調用

ThreadPool.Instance.add(new Thread());

能夠看出使用枚舉方式,代碼比較簡潔並且能夠絕對防止屢次實例化,是一個實現Singleton的很是好的方法。

相關文章
相關標籤/搜索