Working with Executor Framework

1. Understanding Executor Interface

MethodDetail
execute(Runnable)Submit fire-and-forget
ImplementationsThreadPoolExecutor, ForkJoinPool, etc.

2. Using ExecutorService

MethodReturns
submit(Runnable)Future<?>
submit(Callable)Future<V>
invokeAll(tasks)List of Futures
invokeAny(tasks)First successful
shutdown()Graceful
shutdownNow()Interrupt running
awaitTermination(t,u)Wait for completion

3. Creating Fixed Thread Pool

Example: Fixed pool

try (ExecutorService pool = Executors.newFixedThreadPool(8)) {
    var futures = tasks.stream().map(pool::submit).toList();
    for (var f : futures) results.add(f.get());
}   // auto-shutdown via Java 19+ AutoCloseable
AspectDetail
ThreadsFixed count, reused
QueueUnbounded LinkedBlockingQueue (memory risk)
UseCPU-bound, predictable load

4. Creating Cached Thread Pool

AspectDetail
Threads0 to Integer.MAX_VALUE
Idle timeout60s
QueueSynchronousQueue (no buffering)
RiskUnbounded thread creation under load

5. Creating Scheduled Thread Pool

Example: Scheduled tasks

ScheduledExecutorService sched = Executors.newScheduledThreadPool(2);
sched.schedule(task, 5, TimeUnit.SECONDS);
sched.scheduleAtFixedRate(task, 0, 1, TimeUnit.MINUTES);
sched.scheduleWithFixedDelay(task, 0, 30, TimeUnit.SECONDS);
MethodBehavior
schedule(t,d,u)One-shot delay
scheduleAtFixedRatePeriod from start
scheduleWithFixedDelayDelay between completions

6. Creating Single Thread Executor

AspectDetail
ThreadsExactly one
UseSequential ordering guarantee
ReplacementIf thread dies, a new one is created

7. Creating Virtual Thread Per Task Executor JAVA 21+

Example: Virtual threads

try (ExecutorService vexec = Executors.newVirtualThreadPerTaskExecutor()) {
    IntStream.range(0, 100_000).forEach(i ->
        vexec.submit(() -> { fetchUrl(i); return null; })
    );
}
AspectDetail
CostLightweight — millions of threads OK
Best forI/O-bound (HTTP, DB) workloads

8. Submitting Tasks

MethodBehavior
execute(r)No future returned
submit(r/c)Returns Future
RejectedRejectedExecutionException (after shutdown / queue full)

9. Using Future Interface

MethodDetail
get() / get(t, u)Block for result
isDone() / isCancelled()Status
cancel(mayInterrupt)Try cancel
ExceptionExecutionException wraps task exception

10. Cancelling Tasks

AspectDetail
Pre-executionRemoves from queue
During executionSets interrupt flag (if mayInterrupt)
Tasks must checkThread.interrupted()

11. Shutting Down Executors

MethodBehavior
shutdown()No new tasks; finish queued
shutdownNow()Interrupt running, return queued
close() JAVA 19+shutdown + awaitTermination

12. Configuring ThreadPoolExecutor

Example: Custom pool

var pool = new ThreadPoolExecutor(
    4,                                  // corePoolSize
    16,                                 // maximumPoolSize
    60L, TimeUnit.SECONDS,              // keepAliveTime
    new ArrayBlockingQueue<>(100),      // bounded queue
    new ThreadFactoryBuilder().setNameFormat("worker-%d").build(),
    new ThreadPoolExecutor.CallerRunsPolicy()    // backpressure
);
ParamDetail
corePoolSizeAlways-on threads
maximumPoolSizeUpper bound
keepAliveTimeIdle timeout for above-core threads
workQueueUse bounded for safety
RejectedExecutionHandlerAbort/CallerRuns/Discard/DiscardOldest

13. Using ForkJoinPool

AspectDetail
AlgorithmWork-stealing
Common poolUsed by parallel streams & CompletableFuture
TasksRecursiveTask / RecursiveAction

14. Implementing Producer-Consumer Pattern

Example: Producer-Consumer

BlockingQueue<Item> queue = new LinkedBlockingQueue<>(100);

Runnable producer = () -> {
    while (running) queue.put(produce());
};
Runnable consumer = () -> {
    while (true) process(queue.take());
};
ElementRole
BlockingQueueBounded buffer
Producerput (blocks if full)
Consumertake (blocks if empty)
Poison pillSentinel for shutdown