注意這裡的原子性不是資料庫事務中的原子性。這裡原子性的定義是這樣的:一個操作或一系列是不可中斷的。即使是在多個線程一起執行的時候,這些操作一旦開始,就不會被其他線程幹擾。
正确同步的程式:臨界區内代碼的執行具有原子性。
單個volatile變量的讀/寫具有原子性。
任意的單個變量的讀操作具有原子性。
使用CAS對一個共享變量執行操作具有原子性。
i++複合操作。
多線程中未正确同步的代碼。
JMM中不保證:在32位的處理器上對64位的long型和double型變量的寫操作具有原子性。(當JVM在這種處理器上運作時,可能會把一個64位long/double型變量的寫
操作拆分為兩個32位的寫操作來執行)。
上面我們說過i++的複合操作不具有原子性。那麼i++操作為什麼不具有原子性呢?因為i++這個操作至少可以分為下面這三個步驟:先讀i的i值,對i的值進行加1的操作,然後将修改過i的值寫入到記憶體中。即讀--改--寫操作。這樣在并發程式設計中就會出現問題了,如下所示:

CPU1和CPU2同時從各自的緩存中讀取變量i,分别進行加1的操作,然後分别寫入系統記憶體中。我們之前可能的期望可能是:CPU1+1,CPU2+1結果應該是+2,但是可能的結果确實+1。但是處理器可以通過總線鎖定和緩存鎖定來解決這個問題。簡單說就是:CPU1在讀改寫共享變量的時候,CPU2不能操作緩存了該共享變量記憶體位址的緩存。
所謂總線鎖定就是使用處理器提供的一個LOCK#信号,當一個處理器在總線上輸出此信号時,其他處理器的請求将被阻塞住,那麼該處理器可以獨占共享記憶體。但是總線鎖定會有性能問題,因為在同一時刻,我們隻需保證對某個記憶體位址的操作是原子性即可,但是總線鎖定把CPU和記憶體之間的通信鎖住了,這使得鎖定期間,其他處理器不能操作其他記憶體位址的資料。這個性能上的損耗是非常大的。
在多處理器下,為了保證各個處理器的緩存是一緻的,就會實作緩存一緻性協定,每個處理器通過嗅探在總線 上傳播的資料來檢查自己緩存的值是不是過期了,當處理器
發現自己緩存行對應的記憶體位址被修改,就會将目前 處理器得到緩存行設定成無效狀态,當處理器對這個資料進行修改操作的時候,會重新從系統記憶體中把資料讀到處理器
緩存行中。
頻繁使用的記憶體會緩存在處理器的L1、L2和L3高速緩存裡,那麼如果我們要操作的資料在同一個緩存行裡,那麼我們就可以直接對這個緩存行進行鎖定就行了。是以緩存鎖定是值記憶體區域如果被緩存在處理器的和緩存行中,并且在LOCK操作期間被鎖定,那麼當它執行鎖操作回寫到記憶體時,處理器不在總線上聲言LOCK#信号,而是修改緩存行的記憶體位址,并允許它的緩存一緻性機制來保證操作的原子性。因為緩存一緻性機制會阻止同時修改由兩個以上處理器緩存行的記憶體區域資料,當其他處理器回寫已被鎖定的緩存行的資料時,會使和緩存行無效。
臨界區用來表示一種公共資源或者說是共享資料,它是可以被多個線程使用。但是每一次,隻能有一個線程使用它,一旦臨界區資源被占用,其他線程要想使用這個資源,就必須等待。
java中的并發采用的是共享記憶體模型的方式。共享記憶體是什麼呢?我們知道JVM中堆記憶體和方法區(這裡區分開來了)是線上程之間共享的。 那麼這部分記憶體都包含什麼東西呢?包含執行個體域、靜态域、數組元素等。它們又可以稱為共享變量。 java并發線程之間的通信是由java記憶體模型(JMM)控制的,JMM決定了一個線程對共享變量的寫入何時對另一個線程可見(在預設的情況下,JVM并不要求每個變量在任意時刻都保持同步!)。JMM可以做這樣的一個抽象:
線程之間的共享變量存儲在主記憶體中,每個線程都有一個私有的本地記憶體(或者成為工作記憶體),本地記憶體中存儲了該線程讀-寫共享變量的副本。本地記憶體是JMM的一個抽象概念,它涵蓋了緩存、寫緩沖區、寄存器以及其他的硬體和編譯器優化。 如下圖所示:
可見性是指當一個線程修改了某一個共享變量的值,其他線程能夠立即知道這個修改。我們在并發程式設計遇到了一個大的問題是可見性問題,即一個線程對一個共享變量修改的值,另外一個線程讀不到這個修改後的值。引起可見性問題的原因有很多,像編譯器優化,處理器優化等等。
現代處理器使用寫緩沖區臨時儲存向記憶體寫入的資料,寫緩沖區可以保證指令流水線持續運作,它可以避免由于處理器停頓下來等待向記憶體寫入資料而産生的延遲。同時,通過以批處理的方式 重新整理寫緩沖區,以及合并寫緩沖區對同一記憶體位址的多次寫,減少對記憶體總線的占用。每個處理器上的寫緩沖區,僅僅對它所在的處理器可見(會引起可見性問題)。這個特性會導緻處理器對記憶體的讀/寫操作的執行順序 不一定與記憶體實際發生的讀/寫操作順序一緻。
我們在下一章中會介紹重排序的内容。
參考資料:
Java并發程式設計的藝術。
緩存一緻性協定:
http://www.infoq.com/cn/articles/cache-coherency-primer/
http://blog.csdn.net/muxiqingyang/article/details/6615199