Gymterview
senior

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

Ограниченный буфер (bounded buffer) — это классическая задача «производитель-потребитель» с фиксированной ёмкостью.

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

    private int out; // Индекс для извлечения
    private int in;  // Индекс для вставки

    private final Object isEmpty = new Object(); // Монитор «пусто»
    private final Object isFull = new Object();  // Монитор «полно»

    QueueSynchronized(final int capacity) {
        this.capacity = capacity;
        content = new Object[this.capacity];
        out = 0;
        in = 0;
        size = 0;
    }

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

    @SuppressWarnings("unchecked")
    T get() throws InterruptedException {
        if (size == 0) {
            synchronized (isEmpty) {
                while (size < 1) {
                    isEmpty.wait(); // Ждём, пока буфер не станет непустым
                }
            }
        }
        try {
            synchronized (this) {
                final Object value = content[out];
                content[out] = null;
                if (size > 1) {
                    out = cycleInc(out);
                }
                size--;
                return (T) value;
            }
        } finally {
            synchronized (isFull) {
                isFull.notify(); // Уведомляем производителя, что появилось место
            }
        }
    }

    QueueSynchronized<T> put(T value) throws InterruptedException {
        if (size == capacity) {
            synchronized (isFull) {
                while (size == capacity) {
                    isFull.wait(); // Ждём, пока буфер не освободится
                }
            }
        }
        synchronized (this) {
            if (size == 0) {
                content[in] = value;
            } else {
                in = cycleInc(in);
                content[in] = value;
            }
            size++;
        }
        synchronized (isEmpty) {
            isEmpty.notify(); // Уведомляем потребителя, что появился элемент
        }
        return this;
    }
}

Ключевые моменты реализации:

  • Используется кольцевой буфер (circular buffer) — индексы in и out оборачиваются через cycleInc.
  • Два отдельных монитора (isEmpty, isFull) позволяют производителю и потребителю ожидать независимо.
  • Цикл while в wait() — защита от ложных пробуждений (spurious wakeup).

На собеседовании стоит отметить, что в продакшене вместо ручной реализации используют ArrayBlockingQueue из JDK, который реализует ту же логику, но через ReentrantLock + Condition.