避免死锁的通常建议是始终以相同的顺序锁定资源.但是,对于高度满足的Oracle数据库中的行锁,您将如何实现这一点?
要了解我的意思,请考虑以下示例.一个非常简单的DAO来处理银行账户:
@Component
public class AccountDao {
@Resource
private DataSource dataSource;
public void withdraw(String account, int amount) {
modifyBalance(account, -amount);
}
public void deposit(String account, int amount) {
modifyBalance(account, amount);
}
private void modifyBalance(String account, int amount) {
try {
Connection connection = DataSourceUtils.getConnection(dataSource);
PreparedStatement statement = connection
.prepareStatement("update account set balance = balance + ? where holder = ?");
statement.setInt(1, amount);
statement.setString(2, account);
statement.execute();
}
catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
要在两个帐户之间执行传输,有一种具有传输方法的InternalBankTransfer类:
public void transfer(String from, String to, int amount) {
// start transaction
accountDao.withDraw(from, amount);
accountDao.deposit(to, amount);
// commit transaction
}
通常这很好.但是,假设我们有两个人同时启动转账.让我们说安妮想要在鲍勃想要将50转移到安妮的同时向鲍勃转移100美元.因此在一个线程中,Anne调用transfer(“Anne”,“Bob”,100),而另一个Bob调用transfer(“Bob”,“Anne”,50).如果执行顺序如下,则此代码容易受到死锁的影响:
T1: accountDao.withDraw("Anne", 100);
T2: accountDao.withDraw("Bob", 50);
T1: accountDao.deposit("Bob", 100);
T2: accountDao.deposit("Anne", 50); // BAM! ORA-00060: deadlock detected while waiting for resource
我承认在我开始在实际应用程序中看到死锁之前我根本没有考虑过这个问题.我天真的看法是事务隔离类型自动处理这个问题.甲骨文表示,这是由于应用程序设计不佳造成的.但在这种情况下,什么是好的设计呢?我是否需要选择更新我计划更新的所有内容?如果这是涉及更新多个表的巨大事务怎么办?我应该设计使死锁不可能或只是最小化它们并接受they are a fact of life?