[TOC]java
若是你是一位對設計模式略有接觸的新手,必定會絕不費力的就寫出瞭如下單例代碼(懶漢式單例:等到須要時再實例化):面試
/** * Created by forever on 2017/9/20. */
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
複製代碼
其實上面的代碼就已經涵蓋了單例模式最重要的三個要素:數據庫
然而表面看似完美的代碼,內部其實暗藏殺雞:設計模式
在單線程中看似是沒有什麼問題的,但若是放在多線程的環境中就會有問題了。加入有兩個線程同時訪問getInstance
方法,若是期中一個線程剛進入if (singleton == null){}
裏面,這個時候另外一個線程剛好也訪問這個方法,而且完成建立了一個實例,那個剛剛掛起的那個線程繼續運行的話就會再建立一個實例。那咱們單例的理想不就破滅的了嘛。多線程
既然瞭解了問題,那麼咱們如何才能防止兩個線程同時實例化方法呢?有經驗的同窗或許就會馬上想到了Java的同步。經過synchronized
關鍵字進行加鎖。性能
public synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
複製代碼
不過加鎖的話對程序的性能性能會有很大影響,若是當某個線程正在訪問該方法的時候其餘線程就只能在鎖池中等待該線程釋放鎖,咱們稍加改進一下:網站
public Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
複製代碼
這樣只在構造實例代碼的時候加鎖,對程序的性能影響就小多了。並且只要實例化完成以後,後面基本就不會進入這個同步代碼塊了。spa
看似已經很完美了,那麼咱們有什麼其餘辦法不用加鎖的方式也能避免多線程的問題呢?ok,固然有的,咱們可使用餓漢式的單例:線程
/** * Created by forever on 2017/9/20. */
public class Singleton {
public Singleton singleton = new Singleton();
private Singleton() {
}
public Singleton getInstance() {
return singleton;
}
}
複製代碼
上面代碼與懶漢式加載最大的區別在於這裏的single在開始就實例化了,也就是無論咱們是否使用它,都會將其加載到內存中去。這個在獲取的時候就直接返回就好了。若是不在乎內存的話最好使用這個方法。設計
若是你說,我既不想要使用同步,但又十分在乎內存資源怎麼辦,ok,說明你是一個頗有追求的人,其實在也是有辦法的(辦法總比問題多):
public class Singleton {
private Singleton(){
}
public static Singleton getInstance(){
return Nested.singleton;
}
public static class Nested{
static {
System.out.println("蛤蛤");
}
private static Singleton singleton = new Singleton();
}
}
複製代碼
這個時候咱們就須要一個內部類做爲橋樑了,當咱們getInstance()
時,類加載器纔會去加載Nested
,而後實例化Singleton的實例,若是你對Java的類加載機制有了解的話必定很容易就理解了上述代碼。