1. 三大概念

原子性

一個操作或者多個操作 要麼全部執行並且執行的過程不會被任何因素打斷,要麼就都不執行。
類似於數據庫的事務的原子性,比如在銀行轉賬時,兩個賬戶進行讀寫操作,若不具有原子性則可能導致意想不到的結果。
再舉個例子,一個簡單的賦值語句 i=9,假若執行到這句話包括兩個過程爲低16爲賦值和爲高16爲賦值。那麼有可能發生一個線程修改完低16位,被中斷。另外一個線程又去讀取i的值,這時候就會讀取到錯誤的數據。
 

可見性

當多個線程訪問同一個變量時,一個線程修改了這個變量的值,其他線程能夠立即看得到修改的值。
舉例:

//線程1執行的代碼
int i = 0;
i = 10;

//線程2執行的代碼
j = i;

假若線程1剛執行完i=10這句話還沒來得及寫回,線程2執行了 j=i,就會使得線程2拿到的 i  還是初始化的 0 而不是 10。

這就是可見性問題,線程1修改了 i 的值而線程2沒有立即看到值的變化。 

 

有序性

程序執行的順序按照代碼的先後順序執行
這個問題主要是因爲 指令重排序,處理器爲了提高程序的執行效率,可能會在不影響執行結果的前提下調整語句的執行順序。舉個例子

int a = 10; //語句1
int r = 2;	//語句2
a = a + 3;	//語句3
r = a * a;	//語句4

比如這段代碼,處理器可能會進行指令重排序,讓語句2在語句1前執行,但不會讓語句4在語句3之前執行,因爲那會影響執行結果。
但重排序只是保證了不影響單線程下執行結果,在多線程就有可能存在問題。假設一下情況:


//線程1:
context = loadContext();   //語句1
inited = true;             //語句2

//線程2:
while(!inited ){
  sleep()
}
doSomething(context);

在線程1中,語句1和語句2互相之間沒有依賴,可能會被重排序,使得語句2先執行,這時候如果線程2去判斷時會調出循環執行doSomething,而這時候context還未被初始化,會導致程序出錯。

爲了避免上述存在的不一致問題,JAVA虛擬機定義了一種java內存模型(Java Memory Model,JMM)來屏蔽各個硬件平臺和操作系統的內存訪問差異,以實現讓Java程序在各種平臺下都能達到一致的內存訪問效果。

原子性

在java中,對基本數據類型的變量的讀取和賦值(賦值 常量,而不包括賦值變量)都具有原子性的。

x = 10;        //語句1
y = x;         //語句2
x++;           //語句3
x = x + 1;     //語句4

上述語句中,只有語句1具有原子性。因爲其他的語句都涉及2-3個步驟才能完成,比如語句2需要先讀取x值再將值寫入到y相應的內存中。語句3需要先讀取x值,再加1,最後寫入。要想實現更大範圍操作的原子性,則需要通過synchronized或者lock來實現。

可見性

可見性,java通過volatile關鍵字來實現。
當一個共享變量被volatile修飾時,它會保證修改的值會被立即更新到內存,而讀取時也會直接從內存讀取新值。而普通的共享變量不能保證可見性,因爲普通共享變量被修改之後,什麼時候被寫入主存是不確定的,當其他線程去讀取時,此時內存中可能還是原來的舊值,因此無法保證可見性。
另外,通過synchronized和Lock也能夠保證可見性

有序性

在java中,可以通過volatile關鍵字來保證一定的“有序性”。另外可以通過synchronized和Lock來保證有序性。

發佈了23 篇原創文章 · 獲贊 5 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章