6.22Java多執行緒單例設計模式
阿新 • • 發佈:2021-06-23
設計一個單例模式
類與類之間的關係
目標:對外只有一個物件
介紹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再次監測
*/