在多線程環境裡, 多個線程可以自由操作, 當然就能對同一時例進行存取. 這個情況有時候會造成許多無法預期的結果與不必要的問題. 假設現在是要從銀行帳戶取錢, 確認可用餘額這部分的程序代碼應為 :
- if ( 可用餘額大於提領金額 ) {
- 進行提領動作.
- }
先確認可用餘額再允許是否可以提領. 如果可以提領則可以從可用餘額扣除提領金額. 這樣才不會發生可用餘額變成負數的問題.
但是舉例來說, 假設 :
可用餘額 = 1000 元
預提領金額 = 1000 元
線程A | 線程B
"可用餘額大於提領金額?" |
->是 |
<<<<這裡切換成線程B>>>>
| "可用餘額大於提領金額?"
| -> 是
| "從可用餘額減掉預提領金額"
| -> 可用餘額變成 0
<<<<這裡切換成線程A>>>>
"從可用餘額減掉預提領金額" |
-> 可用餘額變成 0 |
當線程A 和 B同時操作, 由於時間差, 可能發生線程B 夾在 線程A 的 "確認可用餘額" 和 " 減去可用餘額 " 這兩個動作間發生錯誤的預期行為. 這時候需要有一種類似 "交通管制" 來協助防止發生這類的問題. 例如當某個線程正在執行某個部份時, 則限制其他線程不可以執行此部分. 這種像是交通管制的操作就稱為共享互斥或互斥控制, 英文寫作 mutual exclusion. Java 語言在處理線程的共享互斥會使用到關鍵字 synchronized.
synchronized 方法 :
當一個方法加上關鍵字synchronized 聲明後, 就可以讓一個線程操作這個方法. " 讓一個線程操作 "並不是說只能讓一個特定線程操作而已, 而是指同一時間只能讓一個線程執行或存取. 這種現程稱為 synchronized 方法, 又稱同步方法. 以如下Bank類別為範例, API : deposit 與 API : withdraw 這兩個方法都是 synchronized方法 :
- public class Bank{
- private int money;
- private String name;
- public Bank(String n,int m){
- this.name = n;
- this.money = m;
- }
- // 存款
- public synchronized void deposit(int m){
- money+=m;
- }
- // 取款
- public synchronized boolean withdraw(int m){
- if (money >= m) {
- money -= m;
- return true; // 已取款
- } else {
- return false; // 餘額不足
- }
- }
- public String getName(){
- return name;
- }
- }
Bank使用說明 :
synchronized 阻擋 :
如果只想在方法裡某一部分的, 而非整個方法阻擋多線程讀取, 則可以使用 synchronized 區塊, 其格式如下 :
- synchronized (表達式) {
- 同步的部分;
- }
* synchronized 實例方法 與 synchronized 區塊
假設現在有一個類型如下的synchronized 實例方法 :
- synchronized void method(){
- ...
- }
- void method(){
- synchronized (this) {
- ...
- }
- }
* synchronized 類方法 與 synchronized 區塊
假設現在有一個類型如下的synchronized 類方法 :
- class Something {
- static synchronized void method(){
- ...
- }
- }
- class Something {
- static void method(){
- synchronized (Something.class){
- ...
- }
- }
- }
補充資料 :
@. [Java] Synchronized 心得
block應該翻區塊比較好,不然翻阻擋好奇怪。
回覆刪除>> block應該翻區塊比較好,不然翻阻擋好奇怪
回覆刪除感謝, 以更正