java併發之原子性、可見性、有序性

原子性

原子性:即一個操做或者多個操做 要麼所有執行而且執行的過程不會被任何因素打斷,要麼就都不執行。多線程

在Java中,對基本數據類型的變量的讀取和賦值操做是原子性操做,即這些操做是不可被中斷的,要麼執行,要麼不執行。併發

上面一句話雖然看起來簡單,可是理解起來並非那麼容易。看下面一個例子:spa

請分析如下哪些操做是原子性操做:線程

1  x = 10; //語句1排序

2  y = x; //語句2內存

3  x++; //語句3編譯器

4  x = x + 1; //語句4同步

咋一看,有些朋友可能會說上面的4個語句中的操做都是原子性操做。其實只有語句1是原子性操做,其餘三個語句都不是原子性操做。編譯

語句1是直接將數值10賦值給x,也就是說線程執行這個語句的會直接將數值10寫入到工做內存中。變量

語句2實際上包含2個操做,它先要去讀取x的值,再將x的值寫入工做內存,雖然讀取x的值以及 將x的值寫入工做內存 這2個操做都是原子性操做,可是合起來就不是原子性操做了。

一樣的,x++和 x = x+1包括3個操做:讀取x的值,進行加1操做,寫入新的值。

因此上面4個語句只有語句1的操做具有原子性。

也就是說,只有簡單的讀取、賦值(並且必須是將數字賦值給某個變量,變量之間的相互賦值不是原子操做)纔是原子操做。

不過這裏有一點須要注意:在32位平臺下,對64位數據的讀取和賦值是須要經過兩個操做來完成的,不能保證其原子性。可是好像在最新的JDK中,JVM已經保證對64位數據的讀取和賦值也是原子性操做了。

從上面能夠看出,Java內存模型只保證了基本讀取和賦值是原子性操做,若是要實現更大範圍操做的原子性,能夠經過synchronized和Lock來實現。因爲synchronized和Lock可以保證任一時刻只有一個線程執行該代碼塊,那麼天然就不存在原子性問題了,從而保證了原子性。


可見性

可見性是指當多個線程訪問同一個變量時,一個線程修改了這個變量的值,其餘線程可以當即看獲得修改的值。

於可見性,Java提供了volatile關鍵字來保證可見性。

當一個共享變量被volatile修飾時,它會保證修改的值會當即被更新到主存,當有其餘線程須要讀取時,它會去內存中讀取新值。

而普通的共享變量不能保證可見性,由於普通共享變量被修改以後,何時被寫入主存是不肯定的,當其餘線程去讀取時,此時內存中可能仍是原來的舊值,所以沒法保證可見性。

另外,經過synchronized和Lock也可以保證可見性,synchronized和Lock能保證同一時刻只有一個線程獲取鎖而後執行同步代碼,而且在釋放鎖以前會將對變量的修改刷新到主存當中。所以能夠保證可見性。


有序性

有序性:即程序執行的順序按照代碼的前後順序執行。

在Java內存模型中,容許編譯器和處理器對指令進行重排序,可是重排序過程不會影響到單線程程序的執行,卻會影響到多線程併發執行的正確性。

在Java裏面,能夠經過volatile關鍵字來保證必定的「有序性」(具體原理在下一節講述)。另外能夠經過synchronized和Lock來保證有序性,很顯然,synchronized和Lock保證每一個時刻是有一個線程執行同步代碼,至關因而讓線程順序執行同步代碼,天然就保證了有序性。

相關文章
相關標籤/搜索