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();
}
}