我的家園

          我的家園

          如何避免死鎖--JCIPC10讀書筆記

          Posted on 2012-04-15 16:26 zljpp 閱讀(183) 評論(0)  編輯  收藏

          [本文是我對Java Concurrency In Practice C10的歸納和總結. ?轉載請注明作者和出處, ?如有謬誤, 歡迎在評論中指正. ]

          如果多個線程以不同的順序持有多個鎖, 可能發生死鎖:

          ?

          public class AccountTrans {
          	public void transferMoney(Account fromAccount, Account toAccount, DollarAmount amount)
          			throws InsufficientFundsException {
          		synchronized (fromAccount) {
          			synchronized (toAccount) {
          				if (fromAccount.getBalance().compareTo(amount) < 0)
          					throw new InsufficientFundsException();
          				else {
          					fromAccount.debit(amount);
          					toAccount.credit(amount);
          				}
          			}
          		}
          	}
          }

          ?

          transferMoney方法先后鎖定fromAccount和toAccount對象. 如果2個線程以如下的方式調用transferMoney方法:

          A: transferMoney(myAccount, yourAccount, 10);?

          B: transferMoney(yourAccount, myAccount, 20);

          死鎖有可能就會發生.

          關鍵在于需要保證以相同的順序獲取多個鎖:

          ?

          public class AccountTrans {
          	// 額外的鎖
          	private static final Object tieLock = new Object();
          
          	public void transferMoney(final Account fromAcct, final Account toAcct, final DollarAmount amount)
          			throws InsufficientFundsException {
          		class Helper {
          			public void transfer() throws InsufficientFundsException {
          				if (fromAcct.getBalance().compareTo(amount) < 0)
          					throw new InsufficientFundsException();
          				else {
          					fromAcct.debit(amount);
          					toAcct.credit(amount);
          				}
          			}
          		}
          		// 計算fromAcct和toAcct的hashCode值
          		int fromHash = System.identityHashCode(fromAcct);
          		int toHash = System.identityHashCode(toAcct);
          
          		// 根據hashCode值確定獲取鎖的順序
          		if (fromHash < toHash) {
          			synchronized (fromAcct) {
          				synchronized (toAcct) {
          					new Helper().transfer();
          				}
          			}
          		} else if (fromHash > toHash) {
          			synchronized (toAcct) {
          				synchronized (fromAcct) {
          					new Helper().transfer();
          				}
          			}
          		} else {
          			// 當hashCode值相同時, 無法確定fromAcct和頭Acct鎖的獲取順序, 因此增加額外的鎖
          			synchronized (tieLock) {
          				synchronized (fromAcct) {
          					synchronized (toAcct) {
          						new Helper().transfer();
          					}
          				}
          			}
          		}
          	}
          }
          ?

          open call

          所謂open call是指在未持有鎖時調用外部方法. 持有鎖的時候調用外部方法, 如果被調用的方法需要獲取其他的鎖, 可能帶來死鎖的風險. 如果被調用的方法發生阻塞, 當前線程將長時間持有鎖, 其他等待獲取該鎖的線程就會被阻塞.

          因此我們應該盡量在未持有鎖的時候進行方法的調用.

          ?

          資源死鎖

          比如線程A持有數據庫D1的連接, 并等待獲取數據庫D2的連接. 而線程B持有數據庫D2的連接, 并等待獲取數據庫D1的連接. 此時就發生了死鎖.

          資源死鎖的另一種形式是線程饑餓死鎖, 參見第八章.

          ?

          避免死鎖

          1. 盡量不要同時持有多個鎖.

          2. 如果必須同時持有多個鎖, 那么保證以一致的順序獲取鎖.

          3. 盡量在未持有鎖的情況下進行方法的調用(open call).






          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 扶余县| 庆城县| 贵定县| 大田县| 西丰县| 泗水县| 邳州市| 乌鲁木齐市| 大同县| 崇义县| 拉萨市| 河津市| 鹤庆县| 正定县| 武陟县| 武穴市| 东源县| 桃园市| 灵武市| 新龙县| 六盘水市| 安多县| 泰安市| 曲阳县| 山西省| 万源市| 庆云县| 安泽县| 昭通市| 乐安县| 拉孜县| 南充市| 山阳县| 资阳市| 平和县| 平凉市| 绥化市| 大田县| 徐闻县| 永平县| 大姚县|