1. 程式人生 > >JAVA三種實現單例模式方法(二):使用靜態內部類實現單例設計模式

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();
		}
	}
}

其他: