Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java BlockingQueue drainTo: batch size is not a transaction

Last updated: 5 Oct 20264 min read
tutorial
IntermediateBy AITrove Editorial

BlockingQueue.drainTo removes available items into another collection, with an optional maximum item count.

Bound the transfer

A worker can pull up to a declared batch size with drainTo(target, maxElements) and process that in memory. It does not wait for enough items to fill the batch; it transfers what is available at the call. A producer may add more immediately after the transfer. For a worker that must wait for at least one item, use take first and then drain the remainder under a separate batch-size rule.

The destination collection has to accept every transferred element. If its add operation fails, the interface does not promise that the two collections collectively retain exactly one copy of each item. Do not use drainTo as a database commit, an exactly-once handoff, or an atomic move to a failing sink.

Keep retries outside the queue operation

The example uses an ordinary ArrayList destination, then acknowledges the batch in application code. A real worker should write an idempotent result and record which IDs finished before discarding a batch after a crash. If a database is involved, transaction ownership and stored schema changes are separate concerns.

ArrayBlockingQueue has fixed capacity, so its producer admission behavior is visible. Compare a nonblocking queue when producers must reject immediately rather than wait.

Working program

Java
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class DispatchBatchTransfer {
    public static void main(String[] args) {
        BlockingQueue<String> pending = new ArrayBlockingQueue<>(4);
        pending.add("dispatch-47");
        pending.add("dispatch-82");
        pending.add("dispatch-15");
        List<String> batch = new ArrayList<>(2);
        int transferred = pending.drainTo(batch, 2);
        System.out.println(transferred + ":" + batch);
        System.out.println(pending.poll());
    }
}

Output

Output
2:[dispatch-47, dispatch-82]
dispatch-15

Cost and ownership

The transfer touches up to k items and stores O(k) references in the destination. Fixed queue capacity bounds queued references, but batch processing may allocate more state per item. Drain and downstream persistence are distinct operations with a crash window between them.

Common Mistakes

  • Do not assume drainTo waits until maxElements are present.
  • Do not drain into a destination whose add can fail without a recovery rule.
  • Do not claim exactly-once processing from an in-memory queue transfer.

Read next

Java blocking queues: bounded capacity and backpressure, Java ConcurrentLinkedQueue: size is not an admission limit, Java JDBC transactions: atomic updates and rollback, Java cancellation: timed waits and cooperative interruption.

java
blockingqueue-drain-contract
Storage details