到底如何保證線程安全,總結得太好了!
一、線程安全等級
之前的博客中已有所提及“線程安全”問題,一般我們常說某某類是線程安全的,某某是非線程安全的。其實線程安全并不是一個“非黑即白”單項選擇題。
按照“線程安全”的安全程度由強到弱來排序,我們可以將java語言中各種操作共享的數據分為以下5類:不可變、絕對線程安全、相對線程安全、線程兼容和線程對立。

在java語言中,不可變的對象一定是線程安全的,無論是對象的方法實現(xiàn)還是方法的調用者,都不需要再采取任何的線程安全保障措施。如final關鍵字修飾的數據不可修改,可靠性最高。
絕對的線程安全完全滿足Brian GoetZ給出的線程安全的定義,這個定義其實是很嚴格的,一個類要達到“不管運行時環(huán)境如何,調用者都不需要任何額外的同步措施”通常需要付出很大的代價。
相對線程安全就是我們通常意義上所講的一個類是“線程安全”的。另外,多線程系列面試題和答案全部整理好了,微信搜索Java技術棧,在后臺發(fā)送:面試,可以在線閱讀。
它需要保證對這個對象單獨的操作是線程安全的,我們在調用的時候不需要做額外的保障措施,但是對于一些特定順序的連續(xù)調用,就可能需要在調用端使用額外的同步手段來保證調用的正確性。
在java語言中,大部分的線程安全類都屬于相對線程安全的,例如Vector、HashTable、Collections的synchronizedCollection()方法保證的集合。
4、線程兼容
線程兼容就是我們通常意義上所講的一個類不是線程安全的。最新多線程系列面試題整理好了,點擊Java面試庫小程序在線刷題。
線程兼容是指對象本身并不是線程安全的,但是可以通過在調用端正確地使用同步手段來保證對象在并發(fā)環(huán)境下可以安全地使用。Java API中大部分的類都是屬于線程兼容的。如與前面的Vector和HashTable相對應的集合類ArrayList和HashMap等。
5、線程對立
線程對立是指無論調用端是否采取了同步錯誤,都無法在多線程環(huán)境中并發(fā)使用的代碼。由于java語言天生就具有多線程特性,線程對立這種排斥多線程的代碼是很少出現(xiàn)的。
一個線程對立的例子是Thread類的supend()和resume()方法。如果有兩個線程同時持有一個線程對象,一個嘗試去中斷線程,另一個嘗試去恢復線程,如果并發(fā)進行的話,無論調用時是否進行了同步,目標線程都有死鎖風險。正因此如此,這兩個方法已經被廢棄啦。
二、線程安全的實現(xiàn)方法
保證線程安全以是否需要同步手段分類,分為同步方案和無需同步方案。

互斥同步是最常見的一種并發(fā)正確性保障手段。同步是指在多線程并發(fā)訪問共享數據時,保證共享數據在同一時刻只被一個線程使用(同一時刻,只有一個線程在操作共享數據)。
而互斥是實現(xiàn)同步的一種手段,臨界區(qū)、互斥量和信號量都是主要的互斥實現(xiàn)方式。因此,在這4個字里面,互斥是因,同步是果;互斥是方法,同步是目的。
非阻塞的實現(xiàn)CAS(compareandswap):CAS指令需要有3個操作數,分別是內存地址(在java中理解為變量的內存地址,用V表示)、舊的預期值(用A表示)和新值(用B表示)。
CAS指令執(zhí)行時,CAS指令指令時,當且僅當V處的值符合舊預期值A時,處理器用B更新V處的值,否則它就不執(zhí)行更新,但是無論是否更新了V處的值,都會返回V的舊值,上述的處理過程是一個原子操作。
點擊關注公眾號,Java干貨及時送達
這個類的compareAndSet方法作用是首先檢查當前引用是否等于預期引用,并且當前標志是否等于預期標志,如果全部相等,則以原子方式將該引用和該標志的值設置為給定的更新值。
可重入代碼的特點是不依賴存儲在堆上的數據和公用的系統(tǒng)資源、用到的狀態(tài)量都是由參數中傳入、不調用 非可重入的方法等。點擊Java面試庫小程序在線刷題。
(類比:synchronized擁有鎖重入的功能,也就是在使用synchronized時,當一個線程得到一個對象鎖后,再次請求此對象鎖時時可以再次得到該對象的鎖)
2)線程本地存儲
如果一段代碼中所需的數據必須與其他代碼共享,那就看看這些共享數據的代碼是否能保證在同一個線程中執(zhí)行?如果能保證,我們就可以把共享數據的可見范圍限制在同一個線程之內。這樣無需同步也能保證線程之間不出現(xiàn)數據的爭用問題。
符合這種特點的應用并不少見,大部分使用消費隊列的架構模式(如“生產者-消費者”模式)都會將產品的消費過程盡量在一個線程中消費完。
其中最重要的一個應用實例就是經典的Web交互模型中的“一個請求對應一個服務器線程(Thread-per-Request)”的處理方式,這種處理方式的廣泛應用使得很多Web服務器應用都可以使用線程本地存儲來解決線程安全問題。
