文章目錄
JMM(Java Model)
在開始討論java多線程安全機制之前,首先從內存模型來了解一下什么是多線程的安全性。我們都知道java的內存模型中有主內存和線程的工作內存之分,主內存上存放的是線程共享的變量(實例字段,靜態字段和構成數組的元素),線程的工作內存是線程私有的空間,存放的是線程私有的變量(方法參數與局部變量)。線程在工作的時候如果要操作主內存上的共享變量,為了獲得更好的執行性能并不是直接去修改主內存而是會在線程私有的工作內存中創建一份變量的拷貝(緩存),在工作內存上對變量的拷貝修改之后再把修改的值刷回到主內存的變量中去,JVM提供了8種原子操作來完成這一過程:lock, , read, load, use, , store, write。《深入理解java虛擬機-jvm最高特性與實踐》這本書中有一個圖很好的表示了線程,主內存和工作內存之間的關系:
線程安全 - 如果線程執行過程中不會產生共享資源的沖突,則線程安全。
線程不安全 - 如果有多個線程同時在操作主內存中的變量,則線程不安全
什么是線程安全?
java中的線程安全是什么:
就是線程同步的意思,就是當一個程序對一個線程安全的方法或者語句進行訪問的時候,其他的不能再
對他進行操作了,必須等到這次訪問結束以后才能對這個線程安全的方法進行訪問
什么叫線程安全:
如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運
行結果和單線程運行的結果是一樣的,而且其他的變量的值也和預期的是一樣的,
就是線程安全的。
或者說:一個類或者程序所提供的接口對于線程來說是原子操作或者多個線程之間的切換不會導致該接口
的執行結果存在二義性,也就是說我們不用考慮同步的問題。
線程安全問題都是由全局變量及靜態變量引起的。若每個線程中對全局變量、靜態變量只有讀操作,而
無寫操作,一般來說,這個全局變量是線程安全的;若有多個線程同時執行寫操作,一般都需要考慮線
程同步,否則就可能影響線程安全。
存在競爭的線程不安全,不存在競爭的線程就是安全的
實現線程安全的三種方式
1)互斥同步
臨界區:、
信號量
互斥量 mutex
2)非阻塞同步
CAS( And Swap)
3)無同步方案
可重入代碼
使用 類來包裝共享變量,做到每個線程有自己的copy
線程本地存儲
互斥同步鎖(悲觀鎖)
1)
2)
互斥同步鎖也叫做阻塞同步鎖線程標識符 有什么用,特征是會對沒有獲取鎖的線程進行阻塞。
要理解互斥同步鎖,首選要明白什么是互斥什么是同步。簡單的說互斥就是非你即我,同步就是順序訪問。互斥同步鎖就是以互斥的手段達到順序訪問的目的。操作系統提供了很多互斥機制比如信號量線程標識符 有什么用,互斥量,臨界區資源等來控制在某一個時刻只能有一個或者一組線程訪問同一個資源。
Java里面的互斥同步鎖就是和,前者是由語言級別實現的互斥同步鎖,理解和寫法簡單但是機制笨拙,在JDK6之后性能優化大幅提升,即使在競爭激烈的情況下也能保持一個和相差不多的性能,所以JDK6之后的程序選擇不應該再因為性能問題而放棄。
對的優化可以看這篇文章
是API層面的互斥同步鎖,需要程序自己打開并在中關閉鎖,和相比更加的靈活,體現在三個方面:等待可中斷,公平鎖以及綁定多個條件。但是如果程序猿對理解不夠深刻,或者忘記釋放lock,那么不僅不會提升性能反而會帶來額外的問題。另外是JVM實現的,可以通過監控工具來監控鎖的狀態,遇到異常JVM會自動釋放掉鎖。而必須由程序主動的釋放鎖。
互斥同步鎖都是可重入鎖,好處是可以保證不會死鎖。但是因為涉及到核心態和用戶態的切換,因此比較消耗性能。JVM開發團隊在JDK5-JDK6升級過程中采用了很多鎖優化機制來優化同步無競爭情況下鎖的性能。比如:自旋鎖和適應性自旋鎖,輕量級鎖,偏向鎖,鎖粗化和鎖消除。
非阻塞同步鎖
原子類(CAS)
非阻塞同步鎖也叫樂觀鎖,相比悲觀鎖來說,它會先進行資源在工作內存中的更新,然后根據與主存中舊值的對比來確定在此期間是否有其他線程對共享資源進行了更新,如果舊值與期望值相同,就認為沒有更新,可以把新值寫回內存,否則就一直重試直到成功。它的實現方式依賴于處理器的機器指令:CAS( And Swap)
JUC中提供了幾個類以及每個類上的原子操作就是樂觀鎖機制。
在競爭不激烈情況下,性能比略遜,而激烈的時候,也能維持常態。激烈的時候,的性能會優于一倍左右。但是其有一個缺點,就是只能同步一個值,一段代碼中只能出現一個的變量,多于一個同步無效。因為他不能在多個之間同步。
非阻塞鎖是不可重入的,否則會造成死鎖。
無同步方案
1)可重入代碼
在執行的任何時刻都可以中斷-重入執行而不會產生沖突。特點就是不會依賴堆上的共享資源
2)/
線程本地的變量,每個線程獲取一份共享變量的拷貝,單獨進行處理。
3) 線程本地存儲
如果一個共享資源一定要被多線程共享,可以盡量讓一個線程完成所有的處理操作,比如生產者消費者模式中,一般會讓一個消費者完成對隊列上資源的消費。典型的應用是基于請求-應答模式的web服務器的設計