? ? ? ?在網(wǎng)上搜到很多關于什么是線程安全的概念洗鸵,找到的一般都是類似于下面的概念:“如果一個對象可以完全地被多個線程同時使用,那它就是線程安全的”仗嗦,這樣的定義不能說不正確膘滨,可是我們卻無法從中獲取到任何有用的信息。
? ? ? ?在<<深入Java虛擬機>>中看到的定義就嚴謹?shù)亩嘞」铡T娜缦拢?/p>
? ? ? ? 當多個線程訪問同一個對象時火邓,如果不用考慮這些線程在運行時環(huán)境下的調(diào)度和交替運行,也不需要進行額外的同步德撬,或者在調(diào)用方進行任何其他的協(xié)調(diào)操作铲咨,調(diào)用這個對象的行為都可以獲取正確的結(jié)果,那這個對象是線程安全的蜓洪。
? ? ? ? ?為了更加深入理解線程安全鸣驱,我們可以按照線程安全的“安全強度”由強至弱來排序,我們可以將Java語言中各種操作共享的數(shù)據(jù)分為以下5類:不可變蝠咆、絕對線程安全、相對線程安全北滥、線程兼容和線程對立刚操。
1、不可變
不可變的對象一定是線程安全的再芋,并且永遠也不需要額外的同步菊霜。因為一個不可變的對象只要構建正確,其外部可見狀態(tài)永遠也不會改變济赎,永遠也不會看到它處于不一致的狀態(tài)鉴逞。Java
類庫中大多數(shù)基本數(shù)值類如Integer、String和BigInteger都是不可變的司训。
2构捡、線程安全
由類的規(guī)格說明所規(guī)定的約束在對象被多個線程訪問時仍然有效,不管運行時環(huán)境如何排列壳猜,線程都不需要任何額外的同步勾徽。這種線程安全性保證是很嚴格的——許多類,如Hashtable
或者 Vector 都不能滿足這種嚴格的定義统扳。
3喘帚、有條件的線程安全(相對線程安全)
有條件的線程安全類對于單獨的操作可以是線程安全的,但是某些操作序列可能需要外部同步咒钟。條件線程安全的最常見的例子是遍歷由 Hashtable 或者 Vector 或者返回的迭代器——由這些類返回的 fail-fast 迭代器假定在迭代器進行遍歷的時候底層集合不會有變化吹由。為了保證其他線程不會在遍歷的時候改變集合,進行迭代的線程應該確保它是獨占性地訪問集合以實現(xiàn)遍歷的完整性朱嘴。通常倾鲫,獨占性的訪問是由對鎖的同步保證的——并且類的文檔應該說明是哪個鎖(通常是對象的內(nèi)部監(jiān)視器(intrinsic monitor))。
如果對一個有條件線程安全類進行記錄,那么您應該不僅要記錄它是有條件線程安全的级乍,而且還要記錄必須防止哪些操作序列的并發(fā)訪問舌劳。用戶可以合理地假設其他操作序列不需要任何額外的同步。
4玫荣、線程兼容
線程兼容類不是線程安全的甚淡,但是可以通過正確使用同步而在并發(fā)環(huán)境中安全地使用。這可能意味著用一個synchronized 塊包圍每一個方法調(diào)用捅厂,或者創(chuàng)建一個包裝器對象贯卦,其中每一個方法都是同步的(就像 Collections.synchronizedList() 一樣)。也可能意味著用synchronized 塊包圍某些操作序列焙贷。為了最大程度地利用線程兼容類撵割,如果所有調(diào)用都使用同一個塊,那么就不應該要求調(diào)用者對該塊同步辙芍。這樣做會使線程兼容的對象作為變量實例包含在其他線程安全的對象中啡彬,從而可以利用其所有者對象的同步。
許多常見的類是線程兼容的故硅,如集合類 ArrayList 和 HashMap 庶灿、java.text.SimpleDateFormat 、或者 JDBC 類 Connection 和 ResultSet 吃衅。
5往踢、線程對立
線程對立是那些不管是否采用了同步措施,都不能在多線程環(huán)境中并發(fā)使用的代碼徘层。線程對立很少見峻呕,而且通常都是有害的,應當盡量避免趣效。
? ? ? ?一個線程對立的例子是Thread類的suspend()和resume()方法瘦癌,如果有兩個線程同時持有一個線程對象,一個嘗試去中斷線程跷敬,另一個嘗試去恢復線程佩憾,如果并發(fā)進行的話,無論調(diào)用時是否進行了同步干花,目標線程都是存在死鎖風險的妄帘,如果suspend()中斷的線程就是即將要執(zhí)行resume()的那個線程,那就肯定要產(chǎn)生死鎖了池凄。也正是由于這個原因抡驼,suspend()和resume()方法已經(jīng)被JDK聲明廢棄了。常見的線程對立的操作還有System.setIn(),System.setOut()和System.runFinalizersOnExit()等肿仑。