多任務處理在現代計算機作業系統中幾乎已是一項必備的功能了。在許多情況下,讓計算機同時去做幾件事情,不僅是因為計算機的運算能力強大了,還有一個很重要的原因是計算機的運算速度與它的存儲和通信子系統速度的差距太大,大量的時間都花費在磁盤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變量。其實,可見性其實可以看成是一種機制,線程在進入/退出同步塊程式時,它将發送/接收一個變量的更改。
JMM有序性
有序性指線上程内看方法的執行,所有的指令都是有序的,都按照一種串行方式執行。而線上程内觀察其他線程,所有指令都是無序的,指令都可能交叉執行。Java中提供了volatile和synchronized兩個關鍵詞保證線程之間操作的有序性,而這個有序性僅僅是相對的,volatile禁止指令重排序,synchronized則保證持有同一個鎖的同步塊隻能串行運作。
JMM原子性
Java記憶體模型保證了read、load、assign、use、store、write等操作具有原子性,我們可以認為除了long和double類型外,對其他基本資料類型所對應的記憶體單元的通路讀寫都是原子的。但由于這個原子性的顆粒度太小,通常情況下我們需要更大顆粒度的原子性,這時就需要用鎖來保證了。
## 總結
JMM可以說是Java的基礎,也是Java多線程的基礎,它的定義将直接影響JVM及Java多線程實作的機制。要想深入了解多線程并發中的相關問題現象,對Java記憶體模型的深入研究是必不可少的。它的定義必須考慮下面幾個方面,其一是如何更加有效地提高線程的性能效率;其二是如何将底層實體硬體及作業系統的差異屏蔽掉提供統一的對外概念;最後是如何使它的模型既嚴謹又寬松,保證語義不會産生歧義和一些優化擴充。