Java 併發編程:多線程併發內存模型

多任務處理在現代計算機操做系統中幾乎已經是一項必備的功能了。在許多狀況下,讓計算機同時去作幾件事情,不只是由於計算機的運算能力強大了,還有一個很重要的緣由是計算機的運算速度與它的存儲和通訊子系統速度的差距太大,大量的時間都花費在磁盤I/O、網絡通訊或者數據庫訪問上。若是不但願處理器在大部分時間裏都處於等待其餘資源的狀態,就必須使用一些手段去把處理器的運算能力「壓榨」出來,不然就會形成很大的浪費,而讓計算機同時處理幾項任務則是最容易想到、也被證實是很是有效的「壓榨」手段數據庫

 關於可見性

在多核多線程環境中,爲了提高運行時數據訪問性能,常常會使用多層緩存策略。這種架構也就帶來了共享變量可見性問題,每一個核或線程都有本身的本地副本,對於共享變量的讀寫操做可能不會被其它核或線程知曉,也就是不可見了。緩存

 

 Java內存模型

Java的世界也有屬於它本身的內存模型,Java內存模型(Java Memory Model),簡稱JMM。因爲Java被定義成一種跨平臺的語言,因此在內存的描述上面也要能是跨平臺的,Java虛擬機試圖定義一種統一的內存模型,能將各類底層硬件及操做系統的內存訪問差別進行封裝,使Java程序在不一樣硬件及操做系統上都能達到相同的併發效果。它描述了程序中各個變量之間的關係,包括實例域、靜態域、數據元素及在實際計算機系統中將變量存儲到內存和從內存中取出變量的底層細節。  網絡

爲更好理解JMM的工做機制,咱們經過下圖進行理解。從總體上看有幾個比較重要的概念:主內存、工做(本地)內存、共享變量、共享變量副本、線程等。首先看主內存與工做內存及他們的關係,主內存保存了Java程序的全部變量,固然這個變量不包括局部變量和方法參數。工做內存則包含了這些共享變量的副本。其次是線程與工做內存的關係,每一個線程都有一個屬於本身的工做內存,不一樣線程之間的工做內存是互相不可見的,且線程對變量的操做也只能是針對本身的工做內存。最後是關於線程之間的通訊機制,線程之間不可直接傳遞。假如某個線程對一個變量進行了從新賦值,那麼該如何讓另外一個條線程知道呢?線程A將變量改變反應到主存中,線程B再從主存中讀取,這樣才能完成線程之間的通訊。多線程

 

 JMM主要操做

JMM定義了八個主要的內存操做來完成工做內存與主存的通訊。假如一條線程準備對一個變量進行新的賦值操做,它可能會先用lock操做鎖住主內存中的某個變量,不讓其餘線程得到此變量的鎖,直至使用unlock操做釋放該變量的鎖。接着使用read操做將變量從主存讀到工做內存中,緊接着load操做將獲得的變量值放到工做內存中的變量副本。use操做則將變量值傳給線程執行引擎進行運算操做,assign操做把新的變量值從線程執行引擎中傳遞到工做內存。store操做則把變量值從工做內存傳送到主存中,接着write操做將獲得的值寫入主存相應的變量中,最後使用unlock操做釋放變量的鎖。架構

 

 JMM可見性

在Java內存模型中,若是一個線程更改了共享變量的值,其餘線程能立刻知道這個更改,則咱們說這個變量具備可見性。通常來講有四種方式能保證變量的可見性,分別爲volatile、synchronized、final和鎖。  併發

首先談談volatile,被此關鍵詞聲明的變量,每當有任何更改時都將當即同步到主存中,而每一個線程要使用這個變量時都要從新從主存刷新到工做內存,這樣就確保了變量的可見性。固然,普通變量最終也會同步到主存,再由主存同步到每一個線程的工做內存,只是這個最終可能比較「長久」,不能保證可見性。  性能

其次,關於synchronized,因爲synchronized底層也是經過鎖進行實現,因此synchronized和鎖的本質是同樣的。當一個線程釋放一個鎖時,將會強制刷新工做內存中的變量值到主存中。而當另外一個線程獲取此鎖的時候將會強制從新裝載此變量值。固然這兩個線程獲取的是同一個鎖,這樣就保證了變量的可見性。  優化

最後,被final聲明的變量一旦完成初始化,其餘線程就能看到這個final變量。其實,可見性其實能夠當作是一種機制,線程在進入/退出同步塊程序時,它將發送/接收一個變量的更改。spa

 JMM有序性

有序性指在線程內看方法的執行,全部的指令都是有序的,都按照一種串行方式執行。而在線程內觀察其餘線程,全部指令都是無序的,指令均可能交叉執行。Java中提供了volatile和synchronized兩個關鍵詞保證線程之間操做的有序性,而這個有序性僅僅是相對的,volatile禁止指令重排序,synchronized則保證持有同一個鎖的同步塊只能串行運行。操作系統

 JMM原子性

Java內存模型保證了read、load、assign、use、store、write等操做具備原子性,咱們能夠認爲除了long和double類型外,對其餘基本數據類型所對應的內存單元的訪問讀寫都是原子的。但因爲這個原子性的顆粒度過小,一般狀況下咱們須要更大顆粒度的原子性,這時就須要用鎖來保證了。

 

 總結

JMM能夠說是Java的基礎,也是Java多線程的基礎,它的定義將直接影響JVM及Java多線程實現的機制。要想深刻了解多線程併發中的相關問題現象,對Java內存模型的深刻研究是必不可少的。它的定義必須考慮下面幾個方面,其一是如何更加有效地提升線程的性能效率;其二是如何將底層物理硬件及操做系統的差別屏蔽掉提供統一的對外概念;最後是如何使它的模型既嚴謹又寬鬆,保證語義不會產生歧義和一些優化擴展。

 

相關文章
相關標籤/搜索