1
\$\begingroup\$

A thread-safe queue suspending/resuming the producer when respectively upper/lower watermark has been crossed. Intended usage - interchange between a non-blocking IO loop thread and a consumer running in a different thread/pool. High performance is not a key requirement, but comments in this direction are very welcome.

import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

public final class WatermarkQueue<E> extends AbstractQueue<E> {
    private final Queue<E> queue = new ArrayDeque<>();

    private final int lowerWatermark;
    private final int upperWatermark;

    private final Runnable onSuspend;
    private final Runnable onResume;

    private final AtomicBoolean producerSuspended = new AtomicBoolean(false);

    public WatermarkQueue(int lowerWatermark, int upperWatermark,
                          Runnable onSuspend, Runnable onResume) {
        this.queue = new ArrayDeque<>();
        this.lowerWatermark =lowerWatermark;
        this.upperWatermark = upperWatermark;
        this.onSuspend = onSuspend;
        this.onResume = onResume;
    }

    public WatermarkQueue(int initialQueueCapacity, int lowerWatermark, int upperWatermark,
                          Runnable onSuspend, Runnable onResume) {
        this.queue = new ArrayDeque<>(initialQueueCapacity);
        this.lowerWatermark =lowerWatermark;
        this.upperWatermark = upperWatermark;
        this.onSuspend = onSuspend;
        this.onResume = onResume;
    }


    @Override
    public synchronized Iterator<E> iterator() {
        return new ArrayList<>(queue).iterator();
    }

    @Override
    public synchronized int size() {
        return queue.size();
    }

    @Override
    public synchronized boolean offer(E e) {
        final boolean wasEmpty = queue.isEmpty();
        queue.offer(e);

        if (queue.size() == upperWatermark) {
            if (!producerSuspended.getAndSet(true)) {
                onSuspend.run();
            }
        }

        if (wasEmpty) {
            notify();
        }

        return true;
    }

    @Override
    public synchronized E poll() {
        while (queue.isEmpty()) {
            try {
                wait();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return null;
            }
        }

        final E e = queue.poll();
        if (e != null) {
            if (queue.size() == lowerWatermark) {
                if (producerSuspended.getAndSet(false)) {
                    onResume.run();
                }
            }
        }

        return e;
    }

    @Override
    public synchronized E peek() {
        return queue.peek();
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.