單例模式是設計模式中用得比較多的一種設計模式,它的主要優點有:
1.訪問受控,保證訪問的是唯一的實例。
2.由於只有一個實例,所以節省資源。
缺點:
靈活性低,如果對象的應用場景多變,則不適用單例模式。
如何實現單例模式?
1.1 懶漢模式(線程不安全)
想要實現單例,莫非就是要適用static關鍵字,如下聲明一個對象:
public class SingleTon { private static SingleTon singleTon = null; }
這樣我們就得到了一個類型為SingleTon的靜態變量,接下來,我們就要控制這個類,不能被任意的new出來,這個就是實現了單例模式唯一性的根本,代碼如下:
public class SingleTon { private static SingleTon singleTon = null; //把構造函數設置為private,防止被new實例化 private SingleTon() {} public static SingleTon getInstance() { //每次調用SingleTon.getInstance()時返回的都是singleTon唯一對象 if( singleTon == null ) { singleTon = new SingleTon(); } return singleTon; }
這種模式的單例在非並發環境下是可靠的,我們知道,我們保證類是單例的代碼關鍵是
if(singleTon == null)
並且java中new是不具有原子性的(涉及到賦值問題),所以,在並發環境下,是可能執行了多次的new操作,造成實例非唯一性。
1.2如何解決?
為了解決問題,首先就要明白問題產生的原因:
上述懶漢產生多個實例造成單例失效的原因是在高並發環境下可能同時有2個或以上的線程訪問getInstance()類方法,又因為new操作不具有原子性,所以會導致產生2個實例的問題。
因此,我們可以:
1.通過添加synchronized來修飾getInstance()方法,簡單有效粗暴,但是往往暴力使用同步方法帶來的問題都是一樣的,就是慘重的效率代價~
2.通過添加synchronized來同步部分代碼塊,並且通過volatile來防止指令重排,代碼如下:
public class SingleTon { private static SingleTon singleTon = null; private SingleTon() {} public static SingleTon getInstance() { if( singleTon == null ) { synchronized( SingleTon.class ) { if( singleTon == null ) { singleTon = new SingleTon(); } } } return singleTon; } }
上述方法就是傳說中的DCL雙重檢查鎖定單例(JDK1.5之后的版本)