java併發程式設計中的三個概念

2021-08-10 22:11:12 字數 2202 閱讀 9900

在併發程式設計中,我們通常會遇到以下三個問題:原子性問題,可見性問題,有序性問題。我們先看具體看一下這三個概念:

原子性

原子性:即乙個操作或者多個操作 要麼全部執行並且執行的過程不會被任何因素打斷,要麼就都不執行。

乙個很經典的例子就是銀行賬戶轉賬問題:

比如從賬戶a向賬戶b轉2000元,那麼必然包括2個操作:從賬戶a減去2000元,往賬戶b加上2000元。

試想一下,如果這2個操作不具備原子性,會造成什麼樣的後果。假如從賬戶a減去2000元之後,操作突然中止。然後又從b取出了500元,取出500元之後,再執行 往賬戶b加上2000元 的操作。這樣就會導致賬戶a雖然減去了2000元,但是賬戶b沒有收到這個轉過來的2000元。

所以這2個操作必須要具備原子性才能保證不出現一些意外的問題。

可見性

可見性是指當多個執行緒訪問同乙個變數時,乙個執行緒修改了這個變數的值,其他執行緒能夠立即看得到修改的值。

舉個簡單的例子,看下面這段**:

//執行緒1執行的**

int i = 0;

i = 10;

//執行緒2執行的**

j = i;

假若執行執行緒1的是cpu1,執行執行緒2的是cpu2。由上面的分析可知,當執行緒1執行 i =10這句時,會先把i的初始值載入到cpu1的快取記憶體中,然後賦值為10,那麼在cpu1的快取記憶體當中i的值變為10了,卻沒有立即寫入到主存當中。

此時執行緒2執行 j = i,它會先去主存讀取i的值並載入到cpu2的快取當中,注意此時記憶體當中i的值還是0,那麼就會使得j的值為0,而不是10.

這就是可見性問題,執行緒1對變數i修改了之後,執行緒2沒有立即看到執行緒1修改的值。

有序性

有序性:即程式執行的順序按照**的先後順序執行。舉個簡單的例子,看下面這段**:

int i = 0;              

boolean flag = false;

i = 1; //語句1

flag = true; //語句2

上面**定義了乙個int型變數,定義了乙個boolean型別變數,然後分別對兩個變數進行賦值操作。從**順序上看,語句1是在語句2前面的,那麼jvm在真正執行這段**的時候會保證語句1一定會在語句2前面執行嗎?不一定,為什麼呢?這裡可能會發生指令重排序(instruction reorder)。

下面解釋一下什麼是指令重排序,一般來說,處理器為了提高程式執行效率,可能會對輸入**進行優化,它不保證程式中各個語句的執行先後順序同**中的順序一致,但是它會保證程式最終執行結果和**順序執行的結果是一致的。

比如上面的**中,語句1和語句2誰先執行對最終的程式結果並沒有影響,那麼就有可能在執行過程中,語句2先執行而語句1後執行。

但是要注意,雖然處理器會對指令進行重排序,但是它會保證程式最終結果會和**順序執行結果相同,那麼它靠什麼保證的呢?再看下面乙個例子:

int a = 10;    //語句1

int r = 2; //語句2

a = a + 3; //語句3

r = a*a; //語句4

這段**有4個語句,那麼可能的乙個執行順序是:語句2 語句1 語句4 語句3

那麼可不可能是這個執行順序呢: 語句2 語句1 語句4 語句3

不可能,因為處理器在進行重排序時是會考慮指令之間的資料依賴性,如果乙個指令instruction 2必須用到instruction 1的結果,那麼處理器會保證instruction 1會在instruction 2之前執行。

雖然重排序不會影響單個執行緒內程式執行的結果,但是多執行緒呢?下面看乙個例子:

//執行緒1:

context = loadcontext(); //語句1

inited = true; //語句2

//執行緒2:

while(!inited )

dosomethingwithconfig(context);

從上面可以看出,指令重排序不會影響單個執行緒的執行,但是會影響到執行緒併發執行的正確性。

也就是說,要想併發程式正確地執行,必須要保證原子性、可見性以及有序性。只要有乙個沒有被保證,就有可能會導致程式執行不正確。

參考文章:

併發執行緒的三個重要概念

對基本資料型別的變數讀取和賦值是保證了原子性的,要麼都成功,要麼都失敗,這些操作不可中斷。比如 i 10 a 10 原子性 b a 不滿足 read a 賦值 b c 不滿足 read c add 賦值 c c c 1 不滿足 read c add 賦值 c 可以使用volatile 關鍵字保證可見...

併發程式設計中的三個概念 原子性,可見性,有序性

在併發程式設計中,我們通常會遇到以下三個問題 原子性問題,可見性問題,有序性問題。我們先看具體看一下這三個概念 1.原子性 原子性 即乙個操作或者多個操作 要麼全部執行並且執行的過程不會被任何因素打斷,要麼就都不執行。乙個很經典的例子就是銀行賬戶轉賬問題 比如從賬戶a向賬戶b轉1000元,那麼必然包...

三個重要概念的區分

計算機只能理解機器語言,不能理解高階語言,因此需要將高階語言轉化為計算機語言。依據語言轉化方式的不同,區分出了編譯型語言和解釋型語言。編譯型語言,指的是一次性的將高階語言轉化為計算機語言,此類程式執行前會將源 編譯為機器語言的檔案,如exe格式的檔案。如要執行,直接執行exe檔案即可,不需要再次編譯...