BlockingQueue.drainTo removes available items into another collection, with an optional maximum item count.
Java BlockingQueue drainTo: batch size is not a transaction
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
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
2:[dispatch-47, dispatch-82]
dispatch-15Cost 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.
