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

Spring task executors: reject work when every slot is occupied

Last updated: 30 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

A bounded ThreadPoolTaskExecutor can reject a new task when its worker and queue are both occupied.

Download Spring source kit

Make saturation observable

The source-kit fixture configures one worker, one waiting slot and the default rejection policy. The first receipt dispatch blocks behind a test latch; the second waits in the queue. A third submission throws TaskRejectedException. After release, both accepted tasks finish. That is a check of admission, not delivery.

Spring queues work after core threads are occupied and grows beyond the core size only after a finite queue fills. With a fixed one-thread pool there is no extra worker to create. An unbounded queue can make a configured maximum pool size irrelevant and move overload into memory. The related future lesson covers errors after admission; this lesson covers a failure at submission.

Map rejection to a real contract

A request handler that catches rejection must say whether it persisted a command before returning. If the command exists in an outbox, the worker can retry later. If it only existed as an in-memory Runnable, a successful HTTP acceptance claim would be false. Do not silently switch to CallerRunsPolicy unless the request thread is allowed to execute potentially slow work and its deadline accounts for that cost.

Checked source

Java
executor.setCorePoolSize(1);
executor.setMaxPoolSize(1);
executor.setQueueCapacity(1);
executor.initialize();
Future<?> first = executor.submit(blockedReceiptDispatch);
Future<?> second = executor.submit(queuedReceiptDispatch);
assertThrows(TaskRejectedException.class,
    () -> executor.submit(overflowReceiptDispatch));

Verification boundary

BoundedExecutorContractTest.thirdTaskIsRejectedWhileOneRunsAndOneWaits runs in the downloadable Spring source kit. The excerpt is shortened; the kit contains the complete tests.

Costs and limits

This local test checks one configuration and one saturated instant. Queue memory is O(capacity), plus task-held objects. It does not measure throughput, fairness, shutdown drain, HTTP status mapping or behavior under several application replicas.

Common Mistakes

  • Do not use an unbounded queue when admission must have a limit.
  • Do not report accepted work after an executor rejected it.
  • Do not treat an executor queue as restart-safe storage.

Read next

Spring @Async futures: make worker failure observable to the caller, Spring async work versus durable delivery: separate latency from recovery, Spring transactional outbox: commit a receipt and event row together.

spring
spring-boot
bounded-executor-rejection
Storage details