← Назад к вопросам

Как можно отследить Deadlock?

3.0 Senior🔥 91 комментариев
#Многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Отслеживание Deadlock в Java

Что такое Deadlock

Deadlock (взаимная блокировка) - это ситуация, когда два или более потока ждут друг друга и оба не могут продолжить выполнение. Это происходит, когда:

  1. Поток A владеет монитором 1 и ждет монитора 2
  2. Поток B владеет монитором 2 и ждет монитора 1

Отслеживание deadlock критично для диагностики и решения проблем с многопоточностью.

1. Метод 1: ThreadMXBean и Management API

Это самый программный способ обнаружения deadlock при выполнении:

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;

public class DeadlockDetector {
    public static void detectDeadlock() {
        ThreadMXBean bean = ManagementFactory.getThreadMXBean();
        long[] deadlockedThreads = bean.findDeadlockedThreads();
        
        if (deadlockedThreads != null && deadlockedThreads.length > 0) {
            System.out.println("Deadlock detected!");
            ThreadInfo[] infos = bean.getThreadInfo(deadlockedThreads);
            for (ThreadInfo info : infos) {
                System.out.println("Thread: " + info.getThreadName());
                System.out.println("State: " + info.getThreadState());
            }
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        while (true) {
            detectDeadlock();
            Thread.sleep(5000);
        }
    }
}

2. Метод 2: Полный мониторинг потоков

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.lang.management.ThreadInfo;

public class ThreadDeadlockMonitor {
    public static void printAllThreadInfo() {
        ThreadMXBean bean = ManagementFactory.getThreadMXBean();
        long[] allThreadIds = bean.getAllThreadIds();
        ThreadInfo[] infos = bean.getThreadInfo(allThreadIds, true, true);
        
        for (ThreadInfo info : infos) {
            if (info == null) continue;
            System.out.println("Thread: " + info.getThreadName());
            System.out.println("State: " + info.getThreadState());
            System.out.println("Blocked count: " + info.getBlockedCount());
        }
    }
}

3. Метод 3: Демонстрация Deadlock

public class DeadlockExample {
    static class Account {
        private int balance = 1000;
        synchronized void withdraw(int amount) {
            balance -= amount;
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        Account account1 = new Account();
        Account account2 = new Account();
        
        Thread thread1 = new Thread(() -> {
            synchronized (account1) {
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                synchronized (account2) {
                    account1.withdraw(1);
                }
            }
        });
        
        Thread thread2 = new Thread(() -> {
            synchronized (account2) {
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                synchronized (account1) {
                    account2.withdraw(1);
                }
            }
        });
        
        thread1.start();
        thread2.start();
    }
}

4. Метод 4: jstack - Внешний инструмент

jps
jstack 12345
jstack 12345 > thread_dump.txt
grep -A 5 "deadlock" thread_dump.txt

5. Метод 5: jconsole

jconsole

В интерфейсе:

  1. Выбрать процесс Java
  2. Перейти на вкладку Threads
  3. Нажать Detect Deadlock

6. Метод 6: VisualVM

jvisualvm

Интерфейс предоставляет:

  • Real-time thread состояния
  • Визуализацию deadlock
  • Анализ памяти
  • Профилирование CPU

7. Метод 7: Логирование с timeout

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;

public class SmartLock {
    private ReentrantLock lock = new ReentrantLock();
    private String name;
    
    SmartLock(String name) {
        this.name = name;
    }
    
    void lockWithTimeout(long timeoutMs) throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + 
            " trying to lock " + name);
        
        boolean acquired = lock.tryLock(timeoutMs, TimeUnit.MILLISECONDS);
        if (!acquired) {
            System.out.println("TIMEOUT! Could not acquire " + name);
            throw new InterruptedException("Lock acquisition timeout");
        }
    }
}

8. Лучшие практики для предотвращения Deadlock

// Плохо: Deadlock вероятен
public void transfer(Account from, Account to) {
    synchronized (from) {
        synchronized (to) {
            // May cause deadlock
        }
    }
}

// Хорошо: Всегда одинаковый порядок
public void transfer(Account from, Account to) {
    Account first = from.getId() < to.getId() ? from : to;
    Account second = from.getId() < to.getId() ? to : from;
    
    synchronized (first) {
        synchronized (second) {
            // Deadlock невозможен
        }
    }
}

// Лучше: Используй timeout
private final ReentrantLock lock = new ReentrantLock();

public void operation() throws InterruptedException {
    if (lock.tryLock(1, TimeUnit.SECONDS)) {
        try {
            // операция
        } finally {
            lock.unlock();
        }
    } else {
        throw new TimeoutException("Failed to acquire lock");
    }
}

Заключение

Отслеживание deadlock - это критический аспект отладки многопоточных приложений. Основные методы:

  1. ThreadMXBean - программный анализ
  2. jstack - command-line снимок потоков
  3. jconsole/VisualVM - визуальный анализ
  4. Логирование с timeout - превентивная отладка
  5. Правильное управление блокировками - лучшая защита

Использование timeout и consistentного порядка захвата блокировок предотвращает deadlock эффективнее, чем его отслеживание.