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\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Browse other questions tagged or ask your own question.