1. 程式人生 > 其它 >6.22Java多執行緒單例設計模式

6.22Java多執行緒單例設計模式

6.22Java多執行緒單例設計模式

設計一個單例模式

類與類之間的關係

目標:對外只有一個物件

介紹double-checking單例模式

使用volatile進行鎖定資源

餓漢式:直接例項化了物件

懶漢式:沒有直接例項化物件

DCL單例設計模式例項demo

圖示:

package thread.rearrangement;

/**
* DCL單例模式:
* 1、在多執行緒環境下,對外存在一個物件--->外部不能new物件--->構造器私有化,避免外部new構造器(懶漢式加入併發控制)
* 2、內部提供私有的靜態屬性--->儲存物件的地址
* 3、對外部提供公共的靜態方法--->獲取屬性(該屬性存了物件的地址)
* @since JDK 1.8
* @date 2021/6/22
* @author Lucifer
*/
public class DoubleCheckedLocking {

/*提供私有的靜態屬性*/
private static volatile DoubleCheckedLocking instance;
//如果沒有volatile其他執行緒可能訪問到一個沒有初始化的物件

/*構造器私有化*/
private DoubleCheckedLocking(){

}

/*提供公共的靜態方法訪問私有屬性*/
public static DoubleCheckedLocking getInstance(){

/*如果已經存在物件,直接返回--->doublechecking避免不必要的同步(已經存在物件)*/
if (null!=instance){
/*直接返回結果*/
return instance;
}

/*加入同步塊鎖住類--->class物件*/
synchronized (DoubleCheckedLocking.class){
/*當沒有物件的時候返回物件*/
if (null==instance){

/*建立一個物件*/
instance = new DoubleCheckedLocking();

/*
在這裡考慮指令重排
在例項化一個物件的時候步驟:
1、開闢空間
2、初始化物件資訊(對、塊、棧)
3、返回物件地址給引用
如果構造器很慢,就有可能下面的內容先執行。
舉例:
A還在初始化物件
B已經拿到了引用
就會形成空物件!!!
如何避免?
在屬性前加入volatile保證可見性
*/
}
}

/*返回類物件*/
return instance;

}

public static void main(String[] args) {
Thread t = new Thread(()-> {
System.out.println(DoubleCheckedLocking.getInstance());
});
t.start();

if (t.equals(DoubleCheckedLocking.getInstance())){
System.out.println(DoubleCheckedLocking.getInstance());
}else {
System.out.println("Error!");
}

/*直接輸出DoubleCheckedLocking.getInstance就不會出現指令重排*/
System.out.println(DoubleCheckedLocking.getInstance());

/*
這裡也發生了指令重排的情況
因為Thread是例項化執行緒,DoubleCheckedLocking.getInstance是直接獲取地址
所以先執行了下面的if判斷
所以執行的結果是先列印處了Error後打印出了getInstance的地址
*/

}
}
/*
1、如果兩個執行緒A、B進入執行緒
2、A進入建立物件但是耗時時間長
3、B進入時候A物件沒有寫回主存,導致B也建立了一個物件
這樣就形不成單例設計模式了,所以要加入同步塊
1、鎖住類的.class物件(類的模子)
2、多個執行緒進入,就不會造成多執行緒建立多個物件
如果已經存在物件則不需要等待,所以需要doublechecking再次監測
*/