Будет ли выброшен Exception при Deadlock?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Будет ли выброшен Exception при Deadlock?
Ответ неоднозначен и зависит от контекста: где происходит deadlock (на уровне БД, OS, или JVM) и как настроена система обработки ошибок.
На уровне базы данных
ДА, будет Exception — база данных ВЫЯВЛЯЕТ deadlock и генерирует ошибку для одной из транзакций.
При использовании JDBC или ORM (Hibernate, JPA), эта ошибка преобразуется в исключение:
public void transferMoney(Long accountA, Long accountB, BigDecimal amount) {
try {
// Транзакция A: читает accountA, затем accountB
Account from = accountRepository.findById(accountA);
Account to = accountRepository.findById(accountB);
from.balance -= amount;
to.balance += amount;
accountRepository.save(from);
accountRepository.save(to);
} catch (DataAccessException e) {
// Если произойдёт deadlock с другой транзакцией:
// Будет выброшен DeadlockLoserDataAccessException
if (e.getRootCause() instanceof java.sql.SQLException) {
java.sql.SQLException sqlException = (java.sql.SQLException) e.getRootCause();
if (sqlException.getErrorCode() == 1213) { // MySQL deadlock code
System.out.println("Deadlock detected!");
// Можем попробовать повторить операцию
}
}
throw e;
}
}
Конкретные исключения по СУБД
PostgreSQL:
// Выбросит SQLException с кодом ошибки "40P01"
try {
// Операция в транзакции
} catch (SQLException e) {
if ("40P01".equals(e.getSQLState())) {
System.out.println("PostgreSQL detected deadlock");
}
}
MySQL:
// Выбросит SQLException с кодом ошибки 1213
try {
// Операция в транзакции
} catch (SQLException e) {
if (e.getErrorCode() == 1213) {
System.out.println("MySQL detected deadlock");
}
}
Oracle:
// Выбросит SQLException с кодом ошибки ORA-00060
try {
// Операция в транзакции
} catch (SQLException e) {
if (e.getErrorCode() == 60) {
System.out.println("Oracle detected deadlock");
}
}
Через Hibernate/JPA
Hibernate преобразует ошибки БД в более удобные исключения:
public class TransferService {
@Autowired
private AccountRepository accountRepository;
@Transactional
public void transferWithRetry(Long fromId, Long toId, BigDecimal amount) {
int maxRetries = 3;
int retryCount = 0;
while (retryCount < maxRetries) {
try {
Account from = accountRepository.findById(fromId).orElseThrow();
Account to = accountRepository.findById(toId).orElseThrow();
from.setBalance(from.getBalance().subtract(amount));
to.setBalance(to.getBalance().add(amount));
accountRepository.saveAll(List.of(from, to));
return;
} catch (DataIntegrityViolationException e) {
// Может быть выброшен при deadlock
retryCount++;
if (retryCount >= maxRetries) {
throw new RuntimeException("Failed after " + maxRetries + " retries", e);
}
try {
Thread.sleep(100 * retryCount); // Exponential backoff
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
}
}
}
На уровне OS (Thread Deadlock)
Если deadlock произойдёт между потоками Java (например, взаимные блокировки synchronized блоков), Exception может быть НЕ выброшен:
// Пример thread deadlock
public class ThreadDeadlockExample {
private Object lock1 = new Object();
private Object lock2 = new Object();
public void method1() {
synchronized (lock1) {
System.out.println("Thread 1: Got lock1");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock2) { // Ждёт lock2, но её держит Thread 2
System.out.println("Thread 1: Got lock2");
}
}
}
public void method2() {
synchronized (lock2) {
System.out.println("Thread 2: Got lock2");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock1) { // Ждёт lock1, но её держит Thread 1
System.out.println("Thread 2: Got lock1");
}
}
}
public static void main(String[] args) {
ThreadDeadlockExample example = new ThreadDeadlockExample();
new Thread(example::method1, "Thread-1").start();
new Thread(example::method2, "Thread-2").start();
// Программа просто зависнет! Exception не будет выброшен
// Только Thread.dump покажет deadlock
}
}
В этом случае Exception НЕ выбросится, программа просто зависнет.
Как обнаружить thread deadlock
public void detectDeadlock() {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
if (deadlockedThreads != null && deadlockedThreads.length > 0) {
System.out.println("Deadlock detected! Affected threads: "
+ Arrays.toString(deadlockedThreads));
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(deadlockedThreads);
for (ThreadInfo threadInfo : threadInfos) {
System.out.println(threadInfo.getThreadName() + " is waiting for "
+ threadInfo.getLockName());
}
}
}
Итоговая таблица
| Тип Deadlock | Exception выбросится? | Как обработать |
|---|---|---|
| Database Deadlock | ДА (SQLException) | Catch & retry, или логирование |
| Thread Deadlock | НЕТ | Thread dump, mониторинг, проектирование |
| Timeout Deadlock | ДА (TimeoutException) | Configurate timeout, retry |
Рекомендации
- Избегайте deadlock-ов через правильный порядок блокировок
- Всегда обрабатывайте исключения БД в транзакциях
- Используйте retry logic с exponential backoff
- Мониторьте deadlock-и на production
- Для thread deadlock-ов проектируйте код без циклических зависимостей
- Используйте timeout-ы при работе с блокировками
Вывод: deadlock на уровне БД выбросит Exception, но deadlock между потоками в Java может просто заморозить приложение без исключения.