ReentrantLock в Java: забытый unlock и блокировка потоков

Основная проблема в данном коде — некорректное использование блокировки ReentrantLock.

Короткий ответ

Что ответить на собеседовании

Основная проблема в данном коде — некорректное использование блокировки ReentrantLock. В методе incrementBins() вызывается lock.lock(), но отсутствует вызов lock.unlock() в блоке finally, что приводит к тому, что блокировка никогда не снимается. В результате при первом вызове блокировки поток захватывает lock и никогда не освобождает его, вызывая блокировку остальных потоков и потенциальный дедлок.

Подробный разбор

Ответ с пояснениями

Условие

В чём основная проблема данной реализации

  • Неэффективное использование фиксированного пула потоков
  • Проблемы с взаимодействием общих ресурсов
  • Некорректное включение блокировки может привести к тупиковой ситуации.
  • Некорректный уровень синхронизации
  • Нет проверок всех потоков перед результатом
  • Нет явного конструктора

Код:

import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;

class RecyclingBinCounter { private int binCount; private final ReentrantLock lock = new ReentrantLock();

public void incrementBins() {
    lock.lock();
    try {
        binCount++;
    } finally {
    }
}

public int getBinCount() {
    return binCount;
}

}

class RecyclingManager { public static void main(String[] args) { RecyclingBinCounter counter = new RecyclingBinCounter(); ExecutorService service = Executors.newFixedThreadPool(3);

    for (int i = 0; i < 3; i++) {
        service.submit(() -> {
            for (int j = 0; j < 1000; j++) {
                counter.incrementBins();
            }
        });
    }

    service.shutdown();
    while(!service.isTerminated()) {}
    System.out.println("Total bins collected: " + counter.getBinCount());
}

}

Ответ

Основная проблема в данном коде — некорректное использование блокировки ReentrantLock. В методе incrementBins() вызывается lock.lock(), но отсутствует вызов lock.unlock() в блоке finally, что приводит к тому, что блокировка никогда не снимается. В результате при первом вызове блокировки поток захватывает lock и никогда не освобождает его, вызывая блокировку остальных потоков и потенциальный дедлок.

Правильный вариант:

public void incrementBins() {
    lock.lock();
    try {
        binCount++;
    } finally {
        lock.unlock();
    }
}

Дополнительно:

  • В main после service.shutdown() лучше использовать service.awaitTermination() с таймаутом вместо пустого цикла while(!service.isTerminated()) {}, чтобы избежать активного ожидания.
  • Можно добавить конструктор в RecyclingBinCounter для инициализации binCount, хотя это не критично.
  • Уровень синхронизации корректен, но без unlock блокировка не работает.

Итог: отсутствие вызова lock.unlock() в finally — главная ошибка, приводящая к блокировке потоков и неправильной работе программы.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее