Java併發程式設計(Java Concurrency)(8)- 競爭與臨界區(Race Conditions and Critical Sections)
原文連結:http://tutorials.jenkov.com/java-concurrency/race-conditions-and-critical-sections.html
摘要:這是翻譯自一個大概30個小節的關於Java併發程式設計的入門級教程,原作者Jakob Jenkov,譯者Zhenning Lang,轉載請註明出處,thanks and have a good time here~~~(希望自己不要留坑)
“競爭”是可能發生在“臨界區”內的一種特使情況。臨界區的含義是:如果多個執行緒訪問一個程式碼段,同時這些執行緒的執行順序將影響總得執行效果,那麼這個程式碼端就叫做臨界區。
反言之,如果臨界區的執行結果受到多執行緒執行順序的影響,那麼就說存在競爭。競爭比喻了不同的執行緒互相爭搶臨界區的程式碼,並且爭搶的結果也將影響臨界區的執行結果。
如果上面過於抽象難懂,下面的例子將幫助理解競爭與臨界區的含義。
1 臨界區
當多個執行緒執行其內部程式碼的時候,其本身不會引起任何問題;引起問題的原因是對共享資源的使用。例如共用的記憶體(變數、陣列和物件等)、系統資源(資料庫、網路服務等)或者是檔案。
跟進一步,事實上只有寫共享資源操作才會引發問題。只要內容不變,讓多個執行緒讀取相同的資源是安全的。
下面是一個臨界區的 Java 程式碼示例,例子中如果多個執行緒同時執行這段程式碼就會發生失敗:
public class Counter {
protected long count = 0;
public void add(long value){
this.count = this.count + value;
}
}
現在假設兩個執行緒 A 和 B 在對相同的 Counter 類的例項進行 add 操作,沒有辦法知道作業系統何時在兩個執行緒間切換。Java 虛擬機器無法像對待單一“原子”一樣處理 add() 函式中的程式碼(就是沒辦法一下子執行完程式碼中的全部內容,而是按步驟執行的),事實上 add() 中的程式碼好像被一系列更小的指令執行:
- 將 this.count 從記憶體中讀到暫存器中
- 將 value 與暫存器中的資料相加並存入暫存器
- 將暫存器資料寫入記憶體
那麼現在考慮如下的執行緒 A 和 B 的執行情況:
this.count = 0; |
B: 將 this.count 讀入一個暫存器 (0) ( |
A: 將 this.count 讀入一個暫存器 (0) |
B: 將暫存器中的資料 +2 (2) |
B: 將暫存器中的資料 (2) 寫回記憶體,此時 this.count 等於 2 |
A: 將暫存器中的資料 +3 (3) |
A: 將暫存器中的資料 (3) 寫回記憶體,此時 this.count 等於 3 |
這兩個執行緒的本來目的是將 2 和 3 加到 counter 上,所以期待的結果應該是 5。然而實際執行中執行緒發生了交錯,導致結果與預期不同。上例中,兩個執行緒都將 0 從記憶體中讀出並且加上了 2 和 3,然後將其寫回記憶體。所以最後的結果取決於誰最後將結果寫回記憶體(2 和 3 都有可能)。
2 臨界區中的競爭
前面例子的 add() 方法中存在著臨界區,所以當多執行緒執行臨界區程式碼的時候,競爭發生了。
對於臨界區和競爭更正規的定義是:如果兩個執行緒爭奪共享資源,並且資源被獲取的時機(順序)將對結果產生影響,這種情況叫做競爭;引發競爭的程式碼段被稱作鄰接區。
3 阻止競爭的發生
為了阻止競爭的發生,臨界區的程式碼必須以“原子”的模式被執行 —— 即一旦一個執行緒開始執行臨界區的程式碼,直到其執行完臨界區,其他的執行緒就無法執行臨界區。
通過對臨界區程式碼設定合理的執行緒同步(thread synchronization)機制,競爭就可以被阻止。而 Java 中的執行緒同步的一種實現方式是同步程式碼塊(a synchronized block of Java code)。其他的實現途徑還有鎖(locks)、原子變數(atomic variables)(例如 java.util.concurrent.atomic.AtomicInteger)。
4 臨界區的吞吐量
相比於定義分散的小的臨界區,將所有程式碼定義成一個大的臨界區也可以讓程式碼正常運作。但將大的臨界區拆封成小的臨界區將帶來更多的好處,因為這樣的話不同的執行緒可以同時執行這些小的臨界區程式碼,從而減少了其相互之間對資源的爭奪,增加了整個臨界區吞吐量。
為了解釋這一點,下面是一個非常簡單的示例:
public class TwoSums {
private int sum1 = 0;
private int sum2 = 0;
public void add(int val1, int val2){
synchronized(this){
this.sum1 += val1;
this.sum2 += val2;
}
}
}
上例中 add() 函式嘗試著將 val1 和 val2 加到 sum1 和 sum2 上。同時為了防止競爭的發生,這段程式碼被放在了 Java 同步程式碼塊中,這樣同一時間只能有一個執行緒執行 add() 中的具體加操作。
然而,由於例子中的 sum1 和 sum2 變數是相互獨立,完全可以分開在兩個同步塊中執行二者的加操作,如下:
public class TwoSums {
private int sum1 = 0;
private int sum2 = 0;
private Integer sum1Lock = new Integer(1);
private Integer sum2Lock = new Integer(2);
public void add(int val1, int val2){
synchronized(this.sum1Lock){
this.sum1 += val1;
}
synchronized(this.sum2Lock){
this.sum2 += val2;
}
}
}
現在兩個執行緒可以同時執行 add() 方法中的程式碼。一個執行緒執行 sum1 的加操作,而另一個執行緒執行 sum2 的加操作。由於兩個同步塊被兩個不同的物件同步(this.sum1Lock 和 this.sum2Lock),這樣一來執行 add() 方法的多個執行緒相互等待的時間就被減少了。
當然上例是非常簡單的,而實際中對於臨界區的拆分可能更加複雜,並且需要仔細考量執行緒執行順序以及其結果的可能。
相關文章
- 【Java系列】Java併發之 Race Condition and Critical SectionJava
- Java 程式設計要點之併發(Concurrency)詳解Java程式設計
- Java併發程式設計實踐(Java concurrency in practice)線上示例學習Java程式設計
- Java併發程式設計---java規範與模式下的併發程式設計1.1Java程式設計模式
- 解決多執行緒競爭條件——臨界區執行緒
- java 併發程式設計Java程式設計
- Java併發程式設計Java程式設計
- Linux 併發與競爭Linux
- 併發程式設計之臨界區\阻塞\非阻塞\死鎖\飢餓\活鎖程式設計
- Java併發程式設計 - 第十一章 Java併發程式設計實踐Java程式設計
- java-併發程式設計Java程式設計
- Java併發程式設計-CASJava程式設計
- Java併發程式設計:synchronizedJava程式設計synchronized
- Java 併發程式設計解析Java程式設計
- Java併發程式設計:LockJava程式設計
- java併發程式設計系列:java併發程式設計背景知識Java程式設計
- Java併發程式設計之Java CAS操作Java程式設計
- Java併發程式設計:Java執行緒Java程式設計執行緒
- 【Java併發程式設計】併發程式設計大合集-值得收藏Java程式設計
- Java併發程式設計-鎖及併發容器Java程式設計
- Java併發系列—併發程式設計挑戰Java程式設計
- Java併發程式設計藝術Java程式設計
- Java併發程式設計實踐Java程式設計
- Java併發程式設計—ThreadLocalJava程式設計thread
- Java 併發程式設計 Executor 框架Java程式設計框架
- Java併發程式設計 -- ConditionJava程式設計
- Java併發程式設計 -- ThreadLocalJava程式設計thread
- Java併發程式設計之synchronizedJava程式設計synchronized
- Java併發程式設計基礎Java程式設計
- Java 併發程式設計要點Java程式設計
- Java併發程式設計——ThreadLocalJava程式設計thread
- java併發程式設計:同步容器Java程式設計
- Java併發程式設計實戰Java程式設計
- Java併發程式設計-synchronized指南Java程式設計synchronized
- 競爭程式設計與實際程式設計的不同程式設計
- Java併發程式設計:Java記憶體模型Java程式設計記憶體模型
- java面試-Java併發程式設計(五)——中斷Java面試程式設計
- 《java學習二》併發程式設計Java程式設計