對象的共享
3.1 可見性:
通常,我們無法確保執(zhí)行讀操作的線程能適時(shí)的看到其他線程寫入的值覆积,為了確保多個(gè)線程之間對內(nèi)存寫入操作的可見性徊都,必須使用同步機(jī)制;
重排序:
在沒有同步的的情況下士飒,編譯器、處理器以及運(yùn)行時(shí)等都可能對操作的執(zhí)行順序進(jìn)行一些意向不到的調(diào)整蔗崎,JVM可以保證在單線程內(nèi)酵幕,程序表現(xiàn)為串行語義(并非沒有重排序,只是不影響結(jié)果)缓苛;因?yàn)橹嘏判蚴且环N優(yōu)化手段芳撒,能提升程序執(zhí)行效率,所以為此犧牲一些易用性是值得的他嫡;
3.1.1 失效數(shù)據(jù):
- 在缺乏同步的程序中番官,讀線程獲得的變量可能是失效值庐完,如讀多個(gè)變量钢属,則可能一部分失效,一部分是最新值门躯;某些失效值可能關(guān)系不大淆党,但鏈表中的引用失效,情況就會非常復(fù)雜讶凉;
3.1.2 非原子的64位操作:
-
非原子的64位操作:當(dāng)線程沒有同步的情況下讀取變量染乌,可能會得到一個(gè)失效值,但至少這個(gè)值是之前某個(gè)線程設(shè)置的值懂讯,而不是一個(gè)隨機(jī)值荷憋,這種安全性保證被稱為最低安全性;最低安全性適用于絕大多數(shù)變量褐望,但是存在一個(gè)例外:
非volatile類型的64位數(shù)值變量(double和long)勒庄;java內(nèi)存模型要求串前,變量的讀取和寫入操作都是原子操作,但對于非volatile類型的double和long變量实蔽,JVM允許將64位的讀操作和寫操作分解為兩個(gè)32位操作荡碾,這意味著,對他們的讀取很可能讀取到某個(gè)值的高32位和另一個(gè)值的低32位局装,因此坛吁,及時(shí)不考慮失效數(shù)據(jù)的我呢提,在并發(fā)程序中使用共享的可變的long和doulbe等類型變量也是不安全的铐尚,除非使用關(guān)鍵字volatile來聲明他們或者用鎖保護(hù)起來拨脉;HotSpot虛擬機(jī)的實(shí)現(xiàn)可能并未拆分為2個(gè)32位操作,但我們編程還是遵循規(guī)范而非針對實(shí)現(xiàn)比較好宣增;
3.1.3加鎖與可見性:
加鎖的含義不僅僅體現(xiàn)在互斥行為女坑,還包括內(nèi)存可見性。為了確保所有的線程都能看見共享變量的最新值统舀,所有的執(zhí)行讀操作和寫操作的線程都必須在同一個(gè)鎖上同步匆骗;
3.1.4 volatile變量:
volatile:
-
volatile的正確使用方式:
- 確保自身的可見性;
- 確保他們所引用對象的狀態(tài)的可見性
- 標(biāo)識一些重要的程序生命周期事件的發(fā)生(如誉简,初始化或關(guān)閉碉就,線程退出等);
加鎖操作既能確泵拼可見性瓮钥,又能確保原子性,而volatile只能確迸氤常可見性碉熄;
調(diào)試tips:
對于服務(wù)器應(yīng)用程序,無論在開發(fā)階段還是在測試階段肋拔,當(dāng)啟動JVM時(shí)锈津,一定都要指定-server命令行選項(xiàng),server模式比client模式的JVM進(jìn)行更多的優(yōu)化凉蜂, 如將循環(huán)內(nèi)部未被修改的變量提升到循環(huán)外部琼梆,因此在開發(fā)環(huán)境中正確運(yùn)行的代碼可能會在部署環(huán)境中運(yùn)行失敗窿吩;如下程序:
volatile boolean interruptted = false;
while(!interruptted){
//do something
}
如果疏忽寫漏了volatile茎杂,client模式JVM可能會表現(xiàn)正常,但server模式下纫雁,程序很有可能死循環(huán)煌往;在應(yīng)用環(huán)境中解決死循環(huán)問題的代價(jià)要大得多;
3.2 發(fā)布與逸出
定義:
發(fā)布一個(gè)對象的意思是指轧邪,使對象能夠在當(dāng)前作用于之外的代碼中使用刽脖,例如悼粮,將一個(gè)指向該對象的引用保存到其他代碼可以訪問的地方,或者在一個(gè)非私有的方法中返回該引用曾棕,或者將引用傳遞到其他類的方法中扣猫;許多情況下,我們需要發(fā)布某個(gè)對象翘地,但如果發(fā)布時(shí)要確保線程安全申尤,則可能需要同步;發(fā)布內(nèi)部狀態(tài)可能會破壞封裝性衙耕。當(dāng)某個(gè)不應(yīng)該發(fā)布的對象被發(fā)布時(shí)昧穿,這種情況就叫逸出;
發(fā)布一個(gè)對象:
當(dāng)發(fā)布某個(gè)對象時(shí)橙喘,可能會間接地發(fā)布其他對象时鸵;該對象的非私有域所引用的對象和非私有方法調(diào)用達(dá)到其他對象,那這些對象也都會被發(fā)布厅瞎,如發(fā)布引用類型數(shù)組對象饰潜,那么該數(shù)組中所有的引用對象都被發(fā)布;
隱式的this引用逸出:向外發(fā)布非靜態(tài)內(nèi)部類同時(shí)會隱式地將this發(fā)布出去和簸,如果該操作在構(gòu)造函數(shù)中彭雾,這將會把未構(gòu)造完成的對象發(fā)布出去,類似的情況還存在于構(gòu)造函數(shù)中調(diào)用可以被重寫的方法锁保,該方法在子類中被重寫薯酝,那么實(shí)例化子類對象時(shí),父類的初始化操作將訪問還未被初始化的子類對象爽柒;
假定有一個(gè)類C吴菠,外部方法指行為不完全由C來控制的方法,包括其他類中定義的方法及C中可以被改寫的方法浩村,當(dāng)把一個(gè)對象傳遞給某個(gè)外部方法時(shí)做葵,就相當(dāng)于發(fā)布了這個(gè)對象;不管外部方法將如何使用該對象穴亏,一旦某個(gè)對象逸出蜂挪,我們都必須假設(shè)有某個(gè)類或者線程可能會吳用該對象重挑,這正是使用封裝的原因嗓化;就如同賬號密碼在網(wǎng)上被人發(fā)布,不管別人是否會惡意使用個(gè)人信息谬哀,但我們的賬戶都已經(jīng)不再安全刺覆;