【JAVA並發編程實戰】8、鎖順序死結,java實戰
package cn.study.concurrency.ch10;public class Account { private String staffAccount; //帳號 private String passWord; //密碼 private int balance; //賬戶餘額 public Account(int money) { this.balance = money; } public String getStaffAccount() { return staffAccount; } public void setStaffAccount(String staffAccount) { this.staffAccount = staffAccount; } public String getPassWord() { return passWord; } public void setPassWord(String passWord) { this.passWord = passWord; } public void debit(int amount) { System.out.println("轉出賬戶:" + amount); } public void credit(int amount) { System.out.println("轉入賬戶:" + amount); } public int getBalance() { return balance; } public void setBalance(int balance) { this.balance = balance; } }
package cn.study.concurrency.ch10;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import javax.naming.InsufficientResourcesException;/** * 通過制定確定的鎖順序來避免死結 * @author xiaof * */public class DeathLock { public void transferMoney(Account fromAccount, Account toAccount, int amount) throws InsufficientResourcesException { synchronized(fromAccount) { synchronized(toAccount) { //按參數的順序上鎖,這個依據參數的調用方法的順序 if(fromAccount.getBalance() < amount) { //賬戶餘額不足,無法轉賬 throw new InsufficientResourcesException(); } else { fromAccount.debit(amount); toAccount.credit(amount); } } } } /** * 這個用來在無法判定枷鎖順序的時候的加時賽鎖 */ private static final Object tieLock = new Object(); public static void transferMoney2(final Account fromAccount, final Account toAccount, final int amount) throws InsufficientResourcesException { /** * 輔助內部類 * @author xiaof * */ class Helper { public void transfer() throws InsufficientResourcesException { //內部類可以隨意訪問外部類成員 //按參數的順序上鎖,這個依據參數的調用方法的順序 if(fromAccount.getBalance() < amount) { //賬戶餘額不足,無法轉賬 throw new InsufficientResourcesException(); } else { fromAccount.debit(amount); toAccount.credit(amount); } } } //返回給定對象的雜湊碼,該代碼與預設的方法 hashCode() 返回的代碼一樣,無論給定對象的類是否重寫 hashCode() int fromHash = System.identityHashCode(fromAccount); int toHash = System.identityHashCode(toAccount); //根據hash值判定加鎖順序,那麼一樣的對象的鎖順序就一定一樣 if(fromHash < toHash) { synchronized(fromAccount) { synchronized(toAccount) { new Helper().transfer(); } } } else if(toHash < fromHash) { synchronized(toAccount) { synchronized(fromAccount) { new Helper().transfer(); } } } else { //如果很不巧,hash值是一樣的,那麼就需要一個加時賽的機制,先擷取外部鎖,然後再此基礎上對兩個對象隨機上鎖 synchronized(tieLock) { synchronized(fromAccount) { synchronized(toAccount) { new Helper().transfer(); } } } } } static Account account1 = new Account(999); static Account account2 = new Account(999); public static void main(String[] args) throws InsufficientResourcesException { //對於第一個方法很容易死結 //比如:當有兩個同時執行這個方法的調用時候// DeathLock dl = new DeathLock(); //這個時候第一個調用在鎖了account1,然後第二個調用鎖了account2 //同時第一個需要account2,第二個需要account1,這就發生競爭死結了// dl.transferMoney(account1, account2, 998);// dl.transferMoney(account2, account1, 998);// // dl.transferMoney2(account1, account2, 998); ExecutorService pool = Executors.newFixedThreadPool(10); for(int i = 0; i < 5; ++ i) { pool.execute(new Runnable() { @Override public void run() { try { DeathLock.transferMoney2(account1, account2, 998); } catch (InsufficientResourcesException e) { e.printStackTrace(); } } }); } for(int i = 0; i < 5; ++ i) { pool.execute(new Runnable() { @Override public void run() { try { DeathLock.transferMoney2(account2, account1, 998); } catch (InsufficientResourcesException e) { e.printStackTrace(); } } }); } pool.shutdown(); }}
測試結果: