線程安全這個名稱,相信稍有經驗的程序員都會聽說過,甚至在代碼編寫和走查的時候可能還會經常掛在嘴邊,并且在面試中是問的最多的,但凡有處理線程安全問題經驗豐富的程序員,都會很吃香。線程安全怎么來定義呢?一本書中曾做過定義:“當多個線程訪問一個對象時,如果不用考慮這些線程在運行時環境下的調度和交替執行,也不需要進行額外的同步,或者在調用方進行任何其他的協調操作,調用這個對象的行為都可以獲得正確的結果,那這個對象是線程安全的”。
今天就來說一說JAVA語言中的線程安全,線程安全問題,無非就是對共享數據的處理,在Java語言中,處理共享數據可以分為:不可變、絕對線程安全、相對線程安全、線程兼容和線程對立。
一、不可變
在Java語言中,不可變 (Immutable)的對象一定是線程安全的,無論是對象的方法實現還是方法的調用者,都不需要再采取任何的線程安全保障措施,只要一個不可變的對象被正確地構建出來,那 其外部的可見狀態永遠也不會改變,永遠也不會看到它在多個線程之中處于不一致的狀態。“不可變”帶來的安全性是最簡單和最純粹的。如果共享數據是一個基本數據類型,那么只要在定義時使用final關鍵字修 飾它就可以保證它是不可變的。如果共享數據是一個對象,那就需要保證對象的行為不會對 其狀態產生任何影響才行,大家都熟悉java.lang.String類的對象,它是一個典型的不可變對象,我們調用它的substring()、replace()和concat()這些方法都不會影響它原來的值,只會返回一個新構造的字符串對象。
保證對象行為不影響自己狀態的途徑有很多種,其中最簡單的就是把對象中帶有狀態的變量都聲明為final,這樣在構造函數結束之后,它就是不可變的,比如java.lang.Integer的構造函數,它通過將內部狀態變量value定義為final來保障狀態不變。
二、絕對線程安全
絕對的線程安全就是要完全滿足:一個類要達到“不管運行時環境如何,調用者都不需要任何額外的同步措施”通常需要付出很大的,甚至有時候是不切實際的代價。在Java API中標注自己是線程安全的類,大多數都不是絕對的線程安全。java.util.Vector是一個線程安全的容器,因為它的add()、get()和size()這類方法都是被synchronized修飾的,盡管這樣效率很低,但確實是安全的。但是,即使它所有的方法都被修飾成同步,也不意味著調用它的時候永遠都不再需要同步手段了。
假如我們在另外一個A方法中調用Vector類中的線程安全的方法,如果 A方法不加synchronized關鍵字,這個A方法也是線程非安全的。
三、相對線程安全
相對的線程安全就是我們通常意義上所講的線程安全,它需要保證對這個對象單獨的操作是線程安全的,我們在調用的時候不需要做額外的保障措施,但是對于一些特定順序的連 續調用,就可能需要在調用端使用額外的同步手段來保證調用的正確性。在Java語言中,大部分的線程安全類都屬于這種類型,例如Vector、HashTable、Collections的synchronizedCollection()方法包裝的集合等。
四、線程兼容
線程兼容是指對象本身并不是線程安全的,但是可以通過在調用端正確地使用同步手段來保證對象在并發環境中可以安全地使用,我們平常說一個類不是線程安全的,絕大多數時候指的是這一種情況。Java API中大部分的類都是屬于線程兼容的,如與前面的Vector和HashTable相對應的集合類ArrayList和HashMap等。
五、線程對立
線程對立是指無論調用端是否采取了同步措施,都無法在多線程環境中并發使用的代碼。由于Java語言天生就具備多線程特性,線程對立這種排斥多線程的代碼是很少出現的,而且通常都是有害的,應當盡量避免。一個線程對立的例子是Thread類的suspend()和resume()方法,如果有兩個線程同時持有一個線程對象,一個嘗試去中斷線程,另一個嘗試去恢復線程,如果并發進行的話,無論調用時是否進行了同步,目標線程都是存在死鎖風險的,如果suspend()中斷的線程就是即將要執行resume()的那個線程,那就肯定要產生死鎖了。也正是由于這個原因,suspend()和resume()方法已經被JDK聲明廢(@Deprecated)了。常見的線程對立的操作還有System.setIn()、Sytem.setOut()和
System.runFinalizersOnExit()等。
面向過程的編程思想極大地提升了現代軟件開發的生產效率和軟件可以達到的規模,但是現實世界與計算機世界之間不可避免地存在一些差異。有時候,良好的設計原則不得不向現實做出一些讓步,我們必須讓程序在計算機中正確無誤地運行,然后再考慮如何將代碼組織得更好,讓程序運行得更快。專業一點講,就是高效并發,高效并發首先需要保證并發的正確性,然后在此基礎上實現高效。所以對于線程并發安全問題的一些基礎知識,我們需要扎實的進行了解。