發布物件:使乙個物件能被當前範圍外
的**所使用。
與之對應的乙個問題是物件逸出
物件逸出是一種錯誤的發布物件方式,當乙個物件還沒有構造完時,就被其他執行緒所見。常見於多執行緒之中。
錯誤的發布物件:
私有成員變數在物件的公有方法中被修改。當其他執行緒訪問該私有變數時可能得到不正確的值。
例如:
private string states = ;
public string getstates()
public static void main(string args) ", arrays.tostring(unsafepublish.getstates()));
unsafepublish.getstates()[0] = "d";
log.info("{}", arrays.tostring(unsafepublish.getstates()));
物件逸出的例子:
在這個內部類中,有對封裝物件的私有成員變數的引用。在物件沒有被正確完成構造之前,它就會被發布。不安全的因素就是在建構函式中顯示的啟動了乙個執行緒,不管是顯示還是隱式的啟動,都會造成this引用的逸出,新執行緒在物件完成構造之前就看到了。
在建構函式中不要直接構造物件,其實就是建立了乙個執行緒,存在上述的逸出風險,
如果要在建構函式中建立執行緒,應該用乙個專有的start或初始化的方法來統一啟動執行緒。可以採用工廠方法 和私有建構函式解決。
總之在物件未完成之前不能將其發布,是安全發布的準則。否則其他執行緒可能會看到舊值,
public class escape
private class innerclass ", escape.this.thiscanbeescape); // 存在逸出風險}}
public static void main(string args)
}
如何安全的發布的?
1、在靜態初始化函式中初始化乙個物件的引用
2、將物件的引用儲存在volatile型別域中
3、將物件的引用儲存在某個正確構造的final型別域中。
4、將物件的引用儲存咋乙個y由鎖保護的域中。
下面是乙個用靜態工廠方法
執行緒不安全的例子
這種方式稱為懶漢模式 :
單例例項在第一次使用時進行建立
該段**在單執行緒中執行是沒有問題的,但是在多執行緒中會存在問題,例如兩個執行緒都開始訪問這個方法時就會出現該例項被例項化兩次
,如果例項化過程中有邏輯運算則返回不同的值。
// 私有建構函式
private singletonexample1()
// 單例物件
private static singletonexample1 instance = null;
// 靜態的工廠方法
public static singletonexample1 getinstance()
return instance;
}
那麼如何保證在多執行緒中保證呢?
餓漢模式:
單例例項在類裝載時進行建立.
由於是類裝載時就就建立例項,因此是執行緒安全的。不過需要注意的是:如果構造方法中有過多的處理,會導致類載入時特別慢。使用餓漢模式只進行載入如果沒有實際呼叫
的話會耗費很多資源。(思考一下,為什麼有的專案啟動需要2-3mins,這過程中載入了多少例項?需要多久?)
因此使用餓漢模式的時候需要注意
1、構造私有函式的時候沒有太多的處理。
2、這個類被載入後肯定會被使用。
3、如果使用靜態域和靜態**塊初一定要注意靜態域和靜態**塊的順序.(靜態域在靜態**塊之前)
// 私有建構函式
private singletonexample2()
// 單例物件
private static singletonexample2 instance = new singletonexample2();
// 靜態的工廠方法
public static singletonexample2 getinstance()
上面的方法也可以用過靜態帶啊模組來實現例項化
// 私有建構函式
private singletonexample6()
// 單例物件
private static singletonexample6 instance = null;
static
// 靜態的工廠方法
public static singletonexample6 getinstance()
public static void main(string args)
深究一下 懶漢模式時候是否可以實現執行緒安全呢?
使用synchronized關鍵字。
該方法 同一時間內只允許乙個執行緒訪問,是可以保證執行緒安全的,但是犧牲了效能 ,競爭激烈的時候會有很多執行緒wait
// 私有建構函式
private singletonexample3()
// 單例物件
private static singletonexample3 instance = null;
// 靜態的工廠方法
public static synchronized singletonexample3 getinstance()
return instance;
}
synchronized修飾方法所造成的的效能問題是否可以再優化呢?
但是可以的,可以將synchronized下沉到方法實現裡面。**如下:在判斷完之後單獨鎖定該類(也就是開始說到如何保證執行緒安全的第四點)通常稱其為雙重同步鎖
單例模式。
這個類是執行緒安全的嗎?
並不是,
instance = new singletonexample4();
當執行完上面這行**時需要三步:
// 1、memory = allocate() 分配物件的記憶體空間
// 2、ctorinstance() 初始化物件
// 3、instance = memory 設定instance指向剛分配的記憶體
在單執行緒下上面邏輯是執行緒安全的,但是多執行緒下會發生指令重排
// jvm和cpu優化,發生了指令重排
// 1、memory = allocate() 分配物件的記憶體空間
// 3、instance = memory 設定instance指向剛分配的記憶體
// 2、ctorinstance() 初始化物件
在指令重排後發生的物件未完成初始化的動作時,卻被直接return。
// 私有建構函式
private singletonexample4()
// 單例物件
private static singletonexample4 instance = null;
// 靜態的工廠方法
public static singletonexample4 getinstance() }}
return instance;
}
如何避免其指令重排呢?
使用關鍵字volatile
和雙重檢查機制可以禁止指令重排。
// 私有建構函式
private singletonexample5()
// 1、memory = allocate() 分配物件的記憶體空間
// 2、ctorinstance() 初始化物件
// 3、instance = memory 設定instance指向剛分配的記憶體
// 單例物件 volatile + 雙重檢測機制 -> 禁止指令重排
private volatile static singletonexample5 instance = null;
// 靜態的工廠方法
public static singletonexample5 getinstance() }}
return instance;
}
併發程式設計(七) 安全發布物件
發布物件是指使乙個物件能夠被當前範圍之外的 所使用 物件逸出是一種錯誤的發布,指當乙個物件還沒有構造完成時,就使它被其他執行緒所見 slf4j public class escape private class innerclass escape.this thiscanbeescape publi...
併發學習(十) 安全物件發布
平時我們建立的物件往往不會考慮到安全物件的概念,這可能比較陌生,但是你面試的時候面試官很喜歡問你執行緒安全的單例模式,而這就是相關的知識點 發布 使物件能夠在除了當前作用域之外的地方使用 最常用的方法 將物件的引用儲存到乙個公有的靜態變數中,讓任何類和執行緒都能看到該物件。逸出 某個物件不應該被發布...
設計執行緒安全的類 VS 發布執行緒安全的物件
步驟 找出構成物件狀態的所有變數 找出約束狀態變數的不變性條件 建立物件狀態的併發訪問策略 1.在現有的執行緒安全類中新增功能 1 重用能減低工作量和提高正確性 2 如果底層的類改變了同步策略,使用不同的鎖來保護它的狀態,則子類會被破壞 class bettervectorextends vecto...