`
coolxing
  • 浏览: 870140 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
9a45b66b-c585-3a35-8680-2e466b75e3f8
Java Concurre...
浏览量:95963
社区版块
存档分类
最新评论

如何避免死锁--JCIPC10读书笔记

阅读更多

[本文是我对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).

2
0
分享到:
评论
1 楼 yuanliangding 2016-08-02  
我的印象中就是
1. 尽量不要同时持有多个锁.
^_^

相关推荐

Global site tag (gtag.js) - Google Analytics