Есть несколько проблем с дизайном выше.
Даже если вы спрашиваете о взаимоблокировках, я чувствую необходимость написать также о других проблемах, которые, по-моему, неверны, и они могут спасти вас от некоторых неприятностей в будущем.
В вашем дизайне первая проблема, которую я вижу, это разделение методов:
Для того, чтобы внести изменения в баланс, у вас есть метод для вывода и способ для депозита. В каждом из них вы используете один и тот же метод "modifyBalance" для выполнения действия. и есть несколько проблем в том, как это делается:
1 - метод modifyBalance запрашивает соединение каждый раз, когда вызывается
2 - в соединении, скорее всего, будет включен режим автоматической фиксации, поскольку вы не отключили автоматическую фиксацию.
почему это проблематично?
логика, которую вы делаете, должна быть единым целым. Предположим, вы забрали 50 у Боба, и это удалось. у вас есть автоматическая фиксация, и изменения являются окончательными. сейчас вы пытаетесь внести депозит Анне, и это не удается. согласно приведенному выше коду, Энн не получит 50, но Боб их уже потерял !!! так что в этом случае вам нужно снова позвонить на Deposit, чтобы вернуть Бобу, и вернуть ему 50, надеясь, что это не сработает, иначе ... бесконечная обработка.
следовательно, эти действия должны быть в одной транзакции. либо снятие и внесение депозита завершаются успешно, и они совершаются, либо они терпят неудачу, и все откатывается.
это также проблематично, поскольку в режиме автоматической фиксации фиксация происходит после завершения оператора или следующего выполнения. если по какой-либо причине фиксация не произошла, то, поскольку вы не закрываете соединение (и это еще одна проблема, поскольку оно не возвращается в пул), и никакая фиксация не происходит, это может привести к взаимоблокировке, если на сервере будет выполнено другое обновление. строка заблокирована в первой транзакции.
поэтому я предлагаю вам сделать следующее: либо запросить соединение в вашем методе перевода, либо объединить методы изъятия и депозита в методе, изменить сам баланс.
Поскольку мне кажется, что вам понравилась идея иметь два метода такими, какие они есть, я продемонстрирую использование первого из упомянутых мной вариантов:)
public class AccountDao {
@Resource
private DataSource dataSource;
public void withdraw(String account, int amount,Connection connection) throws SQLException{
modifyBalance(account, -amount);
}
public void deposit(String account, int amount,Connection connection) throws SQLException{
modifyBalance(account, amount);
}
private void modifyBalance(String account, int amount,Connection connection) throws SQLException {
PreparedStatement statement = connection.prepareStatement("update account set balance = balance + ? where holder = ?");
statement.setInt(1, amount);
statement.setString(2, account);
statement.execute();
}
}
и методом передачи становится:
public void transfer(String from, String to, int amount) {
try {
Connection connection = DataSourceUtils.getConnection(dataSource);
connection.setAutoCommit(false);
accountDao.withDraw(from, amount,connection);
accountDao.deposit(to, amount,connection);
}
catch (SQLException e) {
if (connection!=null)
connection.rollback();
throw new RuntimeException(e);
}
finally {
if (connection!=null){
connection.commit();
connection.close();
}
}
}
Теперь либо оба действия успешны, либо оба откатываются. Кроме того, когда обновление выполняется для строки, другие транзакции, пытающиеся обновить строку, будут ждать ее завершения, прежде чем они смогут продолжить. откат или фиксация обеспечивает снятие блокировки уровня строки.
Теперь вышесказанное является объяснением лучшего дизайна, чтобы сохранить логические действия и правильность данных. но это не решит проблемы блокировки !!!! Вот пример того, что может произойти:
предположим, что кто-то пытается выйти из Боба.
статус: строка bob заблокирована t1
в это время поток 2 выводится из anne
статус: строка anne заблокирована потоком 2
Теперь поток 1 хочет внести в Энн
status: поток 1 видит, что строка anne заблокирована, поэтому он сидит и ожидает снятия блокировки, чтобы он мог выполнить обновление: поток 1 фактически ожидает в потоке два, чтобы завершить обновление и зафиксировать или откатить блокировку выходит на свободу
теперь нить два хочет внести в боб
статус: строка bob заблокирована, поэтому нить 2 ждет своего освобождения
DEADLOCK !!!!!
два потока ждут друг друга.
так как мы решаем это? Пожалуйста, смотрите опубликованные ответы (я видел их при наборе этого) и, пожалуйста, не принимайте этот ответ, но принимайте тот, который вы фактически используете, чтобы предотвратить взаимные блокировки. Я просто хотел поговорить о других проблемах, как раньше, и извините за то, что я так долго.