JAVA三種實現單例模式方法(二):使用靜態內部類實現單例設計模式
靜態程式碼塊和靜態內部類的載入順序:當呼叫外部類的建構函式是,外部類的靜態程式碼塊同時被載入,但是其內部類不會同時被載入;當且僅當內部類的靜態域或其構造方法或其靜態方法被呼叫時,內部內才被載入。
因此,通過內部內實現單例,就能實現延遲載入。
這個解決方案被稱為Lazy initialization holder class 模式,這個模式綜合使用了java的類級內部類和多執行緒預設同步鎖的知識, ,很巧妙的同時實現了延遲載入和執行緒安全。
1 相應的基礎知識
(1)什麼是類級內部類?
簡單點說,類級內部類指的是,有static修飾的成員內部類。如果沒有static修飾的成員式內部類被稱為物件級內部類。
(2)類級內部類相當於其外部類的static成分,它的物件與外部類物件間不存在依賴關係,因此 可以直接建立。而物件級內部類的例項,是繫結在外部物件例項中的。
(3)類級內部類中,可以定義靜態的方法。在靜態方法中只能引用外部類中的靜態成員方法或變數。
(4)類級內部類相當於其外部類的成員,只有在第一次被使用的時候才會被裝載。
多執行緒預設同步鎖的知識:
大家都知道,在多執行緒開發中,為了解決併發問題,主要是通過使用synchronized來加互斥鎖進行同步控制, 但是在某些情況下,JVM已經隱含的為您執行了同步,這些情況下就不用自己再來進行同步控制了。 這些情況包括:
(1)由靜態初始化器(在靜態欄位上或static{}塊中的初始化器)初始化資料時;
(2)訪問final欄位時;
(3)在建立執行緒之前建立物件時;
(4)執行緒可以看見它將要處理的物件時。
2 解決方案的思路
(1)要想很簡單的實現執行緒安全,可以採用靜態初始化器的方式,它可以由JVM來保證執行緒的安全性。比如餓漢式實現方式。但是這樣一來,不是會浪費一定的空間嗎?因為這種實現方式,會在類裝載的時候就初始化物件,不管你需不需要。
(2)如果現在有一種方法能夠讓類裝載的時候不去初始化物件,那不就解決問題了?一種可行的方式就是採用類級內部類,在這個類級內部類裡面去建立物件例項。這樣一來,只要不使用到這個類級內部類, 那就不會建立物件例項,從而同步實現延遲載入和執行緒安全。
//餓漢式單例類.在類初始化時,已經自行例項化
public class Singleton1 {
private Singleton1() {}
private static final Singleton1 single = new Singleton1();
//靜態工廠方法
public static Singleton1 getInstance() {
return single;
}
}
3.補充說明下他是如何體現 懶載入的(Lazy initialization):
因為內部靜態類是要在有引用了以後才會裝載到記憶體的。所以在你第一次呼叫getInstance()之前,SingletonHolder是沒有被裝載進來的,只有在你第一次呼叫了getInstance()之後,裡面涉及到了return SingletonHolder.instance; 產生了對SingletonHolder的引用,內部靜態類的例項才會真正裝載。這也就是懶載入的意思。
關於 “JVM來保證執行緒的安全性” 這句話的意思:
利用了classloader的機制來保證初始化instance時只有一個執行緒,所以也是執行緒安全的,同時沒有效能損耗。
package instance.innerClass;
public class InstanceInnerClass {
private String str;
private Long currentTime;
private static class InnerClass {
private static final InstanceInnerClass instance = new InstanceInnerClass();
}
public static InstanceInnerClass getInstance() {
return InnerClass.instance;
}
private InstanceInnerClass() {
initProperties();
}
private void initProperties() {
this.str = "hello world!";
this.currentTime = System.currentTimeMillis();
}
public void print() {
System.out.println("time:" + currentTime + " >>> " + str );
}
public static void main (String[] args) {
for (int i = 0; i < 3; i++) {
new Thread( new Runnable() {
@Override
public void run() {
InstanceInnerClass instance = InstanceInnerClass.getInstance();
System.out.println("hashCode:" + instance.hashCode());
instance.print();
}
}).start();
}
}
}
其他: