有一次去面試的時候,碰到這樣一個問題邪意,現(xiàn)在我有一個類Person,里面有set和get兩個方法反砌,現(xiàn)在我用多線程訪問這兩個方法雾鬼,怎么保證線程安全。答:syn關鍵字宴树,但是syn關鍵字的參數(shù)如果默認this的話策菜,那么不能保證線程安全,傳遞Person才能保證。
首先做入,對于synchronized關鍵字冒晰,它的機制大概是這個樣子的:synchronized以對象實例或者class對象為鎖,如果synchronized修飾的是方法竟块,首先鎖默認class對象壶运,想要訪問這個方法,那么必須拿到這個鎖浪秘,由于鎖的唯一性(class對象的唯一性決定的)蒋情,所以一次性只能有一個線程訪問這個方法,vector使用的就是使用synchronized修飾了方法耸携。如果synchronized修飾的是代碼塊棵癣,那么需要傳遞一個參數(shù)進去,如果參數(shù)傳遞this夺衍,那么鎖的對象就是當前對象狈谊,如果傳遞的是其他對象實例,那么鎖就是其他對象沟沙。鎖的不同其實決定了線程的安全性河劝。話不多說,上代碼:
對于這一段代碼而言赎瞎,線程安全嗎?vector本身是線程安全的颊咬,但是調(diào)用者不是务甥。這段代碼其實是在深入理解java虛擬機第二版的389頁中喳篇,會出現(xiàn)以下的異常
雖然其出現(xiàn)的概率不高,但是會有這種問題存在。那么問題的本質(zhì)在哪里呢,vector不是線程安全的嗎蜗细?這里不詳細敘述原理踪区,但是問題在于i這個變量缎岗,這是發(fā)生錯誤的場景i<vector.size()判定通過传泊,此時線程執(zhí)行時間結束,保存當前狀態(tài)切換,而此時校读,另一個線程剛好執(zhí)行了remove,跳轉(zhuǎn)回來以后,i的值是已經(jīng)通過判定的却特,但是i其實已經(jīng)過期了,所以此時會報錯。對于不熟悉synchronized的人來說仙蛉,他可能會這么寫
但是其實這么寫完全沒有用(不僅僅沒有解決當前問題夯巷,反而大大降低了程序執(zhí)行的效率),問題出在哪里篮绰,鎖對象喷面。下面是正確的寫法:
這段代碼完美解決了線程安全的問題盒齿,接近或者達到了絕對線程安全的地步边翁,但是效率比上面那段錯誤的代碼還要低。其實還有一種鎖機制ReentrantLock硕盹,這個就不在這里講了符匾。會另外出一篇博客。
其實最開始提出的問題還有一種解決方法瘩例,那就是用syn修飾Person的set()和get()方法啊胶,如果你還用vector并且用的很多,那么這種方法可以使用垛贤,如果你追求更好的效率焰坪,那么請直接舍棄這種方法,因為set和get中的操作一般都很簡單聘惦,加入了鎖那么這個類的操作效率會很低很低某饰。