Gymterview
senior

Напишите простейший многопоточный ограниченный буфер с использованием ReentrantLock

Та же задача «производитель-потребитель», но с использованием ReentrantLock и Condition вместо synchronized и wait()/notify().

Код: кольцевой буфер на ReentrantLock
class QueueReentrantLock<T> {
    private volatile int size = 0;
    private final Object[] content;
    private final int capacity;

    private int out;
    private int in;

    private final ReentrantLock lock = new ReentrantLock();
    private final Condition isEmpty = lock.newCondition(); // Условие «буфер пуст»
    private final Condition isFull = lock.newCondition();  // Условие «буфер полон»

    QueueReentrantLock(int capacity) {
        try {
            lock.lock();
            this.capacity = capacity;
            content = new Object[capacity];
            out = 0;
            in = 0;
        } finally {
            lock.unlock();
        }
    }

    private int cycleInc(int index) {
        return (++index == capacity) ? 0 : index;
    }

    @SuppressWarnings("unchecked")
    T get() throws InterruptedException {
        try {
            lock.lockInterruptibly();
            while (size == 0) {
                isEmpty.await(); // Ждём появления элемента
            }
            final Object value = content[out];
            content[out] = null;
            if (size > 1) {
                out = cycleInc(out);
            }
            size--;
            isFull.signal(); // Уведомляем производителя
            return (T) value;
        } finally {
            lock.unlock();
        }
    }

    QueueReentrantLock<T> put(T value) throws InterruptedException {
        try {
            lock.lockInterruptibly();
            while (size == capacity) {
                isFull.await(); // Ждём освобождения места
            }
            if (size == 0) {
                content[in] = value;
            } else {
                in = cycleInc(in);
                content[in] = value;
            }
            size++;
            isEmpty.signal(); // Уведомляем потребителя
        } finally {
            lock.unlock();
        }
        return this;
    }
}

Преимущества ReentrantLock-версии перед synchronized-версией:

Аспект synchronized версия ReentrantLock версия
Управление условиями Два отдельных объекта-монитора Два Condition от одного Lock
Прерываемое ожидание Нет (wait не прерываем по lockInterruptibly) Да (lockInterruptibly)
Структура кода Множество synchronized блоков Один lock/unlock на метод
Количество блокировок Три (this, isEmpty, isFull) Одна (lock)

На собеседовании часто просят сравнить две реализации. Ключевое преимущество ReentrantLock — единый замок с множественными условиями (Condition), что делает код чище и менее подверженным ошибкам, чем использование нескольких объектов-мониторов.