Java面試:一個單例都有這麼多水,我已經沉下去了

編程語言 Java 設計模式 鎖舞 嘿面試官 2017-04-19

拿出一張A4紙畫出單例模式的UML圖(面試時如果被問到設計模式相關問題,能先畫出UML圖肯定是加分項):

Java面試:一個單例都有這麼多水,我已經沉下去了

單例模式UML圖

估計很多小夥伴都忘記怎麼讀UML圖了吧?這裡詳細解釋下:

  • 實心箭頭是單向關聯關係:這裡表示定義的成員變量instance是類Singleton自身的一個實例

  • ‘-’減號表示private,這裡的成員變量和構造函數都要用private修飾

  • ‘+’加號表示public 對外暴露方法getInstance(),自然要用public static修飾

單例模式具體實現細節,看英文解釋會更直觀:

1.The implementation involves a static member in the "Singleton" class,

靜態成員變量instance

2.a private constructor

私有構造函數

3.a static public method that returns a reference to the static member.

public static修飾的返回一個實例對象的方法

一段簡單的代碼實現:

public final class Singleton {

//類一加載時實例就被創建

private static final Singleton instance = new Singleton();

private Singleton() {

}

public static Singleton getInstance() {

return instance;

}

}

當然如果面試時僅僅寫出上面的代碼是遠遠不夠的,面試官不會輕易讓我們過關的,這只是剛剛熱身,更深入的問題還在後面:

1.你知道懶加載嗎?是怎麼用在單例創建上的?有什麼優勢?

如果某個實例的創建(比如數據庫連接池的創建)需要消耗很多系統資源,就需要引入懶加載機制。即上面的代碼在類加載時就創建好了,如果在程序中始終沒用到這個實例就會浪費很多系統資源。

為避免這種情況,就引入了懶加載機制,即在使用這個實例的時候才創建它。

引入懶加載後的單例實現代碼如下(這就是我們常聽到的“懶漢式”單例):

public class Singleton{

private static Singleton instance = null;

private Singleton(){

}

public static Singleton getInstance(){

if (instance == null)

//實例在調用getInstance()函數的時候才會創建

instance = new Singleton(); //1. A線程執行

return instance; //2.B線程執行

}

}

2.這個例子你考慮到線程安全了嗎?什麼是線程同步?什麼是雙重檢查鎖定?怎麼用在單例上?

的確當引入多線程時,以上代碼不是線程安全的:假設A線程執行代碼1的同時,B線程執行代碼2。此時,線程A可能會看到instance引用的對象還沒有完成初始化。

通過Java語言的關鍵字synchronized實現線程同步,當它用來修飾一個方法或者一個代碼塊的時候,能夠保證在同一時刻最多隻有一個線程執行該段代碼。因此通過對函數getInstance()做同步處理來實現線程安全的初始化:

class Singleton

{

private static Singleton instance;

private Singleton()

{

...

}

public static synchronized Singleton getInstance()

{

if (instance == null)

instance = new Singleton();

return instance;

}

...

public void doSomething()

{

...

}

}

上面的代碼是線程安全的一種實現,但是如果考慮到性能,synchronization是非常消耗資源的,一次只能有一個線程調用getInstance()方法,而getInstance方法在對象實例化之後無需再阻塞訪問。即如果對象已經創建完成,只需要return 這個對象就OK了,不需要用syncronized同步。

因此進一步優化:先在非synchronized塊檢查object是否為null,是就直接return object,否則進入syncronized同步塊創建object,這就成為雙重鎖機制(double locking mechanism).

代碼實現如下:

//Lazy instantiation using double locking mechanism.

class Singleton

{

private static Singleton instance;

private Singleton()

{

System.out.println("Singleton(): Initializing Instance");

}

public static Singleton getInstance()

{

if (instance == null) //1.第一次檢查

{

synchronized(Singleton.class)//2.加鎖

{

if (instance == null) //3.第2次檢查

{

System.out.println("getInstance(): First time getInstance was invoked!");

instance = new Singleton(); //4.實例化對象

}

}

}

return instance;

}

public void doSomething()

{

System.out.println("doSomething(): Singleton does something!");

}

}

相關推薦

推薦中...