PriorityBlockingQueue orders retrieval by priority but is logically unbounded; its constructor argument is an initial capacity, not a maximum backlog.
Java PriorityBlockingQueue: add an admission limit to an unbounded priority queue
Operational contract
The wrapper acquires one of 47 permits before offering a job and releases it when a worker takes that job. This bounds jobs waiting in the queue, not jobs already being processed by workers. The queue comparator orders priority descending and receipt ID ascending for a defined tie break. A failed offer releases the permit. The queue remains private so callers cannot bypass the gate with direct insertion or removal. A production service must also cap producer concurrency and define shutdown and task-failure handling.
Failure case
All 47 waiting slots are filled. A new receipt job is rejected immediately even though PriorityBlockingQueue itself would otherwise accept it and grow. A worker take releases one waiting slot.
Java code
import java.util.Comparator;
import java.util.Objects;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.Semaphore;
public class BoundedPriorityReceiptInbox {
public record Job(int priority, long receiptId) { }
private final Semaphore waitingSlots = new Semaphore(47);
private final PriorityBlockingQueue<Job> jobs = new PriorityBlockingQueue<>(47,
Comparator.comparingInt(Job::priority).reversed()
.thenComparingLong(Job::receiptId));
public boolean submit(Job job) {
Objects.requireNonNull(job);
if (!waitingSlots.tryAcquire()) return false;
boolean added = false;
try {
added = jobs.offer(job);
return added;
} finally {
if (!added) waitingSlots.release();
}
}
public Job take() throws InterruptedException {
Job job = jobs.take();
waitingSlots.release();
return job;
}
}Performance and ownership cost
Insertion and removal are O(log N) heap operations for N queued jobs, capped here at 47 by the permit gate. The gate uses O(1) extra state. Worker execution and producer tasks are outside this queue bound.
Common Mistakes
- Do not treat initial capacity or remainingCapacity as a backlog cap.
- Do not expose the underlying queue for bypass insertion or removal.
- Do not forget that priority service can starve low-priority jobs even with bounded storage.
