Skip to content

FlossWare/jthreadpool

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JThreadPool

Maven Central License: MIT

Managed thread pools with monitoring and graceful shutdown for Java applications.

Features

  • Configurable Thread Pools: Flexible core/max pool sizes, queue capacity, and keep-alive times
  • Thread Naming: Automatic thread naming with application ID for easy identification in thread dumps
  • Exception Handling: Uncaught exception handler that logs errors
  • Statistics Tracking: Real-time pool statistics (active threads, completed tasks, queue size)
  • Graceful Shutdown: Configurable shutdown with timeout and force shutdown fallback
  • AutoCloseable: Implements AutoCloseable for try-with-resources support
  • CallerRunsPolicy: Rejected tasks run in calling thread to prevent task loss

Installation

Maven

<dependency>
    <groupId>org.flossware</groupId>
    <artifactId>jthreadpool</artifactId>
    <version>1.0</version>
</dependency>

Gradle

implementation 'org.flossware:jthreadpool:1.0'

Quick Start

Basic Usage

import org.flossware.jthreadpool.*;

// Create thread pool configuration
ThreadPoolConfig config = ThreadPoolConfig.builder()
    .corePoolSize(4)
    .maxPoolSize(8)
    .keepAliveTimeSeconds(60)
    .queueCapacity(100)
    .build();

// Create managed thread pool
ManagedThreadPool pool = new ManagedThreadPool("my-app", config);

// Submit tasks
pool.submit(() -> {
    System.out.println("Running in thread: " + Thread.currentThread().getName());
    // Do work...
});

// When done
pool.shutdown();

Try-With-Resources

ThreadPoolConfig config = ThreadPoolConfig.defaultConfig();

try (ManagedThreadPool pool = new ManagedThreadPool("my-app", config)) {
    pool.submit(() -> doWork());
    pool.submit(() -> doMoreWork());
    // Pool automatically shuts down when exiting try block
}

Monitoring Statistics

ManagedThreadPool pool = new ManagedThreadPool("my-app", config);

// Submit some tasks
for (int i = 0; i < 10; i++) {
    pool.submit(() -> doWork());
}

// Get current statistics
ThreadPoolStats stats = pool.getStats();
System.out.println("Active threads: " + stats.getActiveThreads());
System.out.println("Completed tasks: " + stats.getCompletedTasks());
System.out.println("Queued tasks: " + stats.getQueuedTasks());
System.out.println("Pool size: " + stats.getPoolSize());

Callable Tasks

Future<String> future = pool.submit(() -> {
    // Do some computation
    return "Result";
});

String result = future.get(5, TimeUnit.SECONDS);
System.out.println("Got result: " + result);

API Overview

ThreadPoolConfig

Configuration builder for thread pool settings.

Builder Methods:

  • corePoolSize(int) - Minimum threads to keep in pool (default: 2)
  • maxPoolSize(int) - Maximum threads allowed in pool (default: 10)
  • keepAliveTimeSeconds(long) - Time idle threads wait before terminating (default: 60)
  • queueCapacity(int) - Maximum queued tasks (default: 100)
  • build() - Build the configuration

Static Methods:

  • defaultConfig() - Returns configuration with default values
  • builder() - Create new builder

Validation:

  • corePoolSize must be >= 0
  • maxPoolSize must be > 0 and >= corePoolSize
  • keepAliveTimeSeconds must be >= 0
  • queueCapacity must be >= 0

ManagedThreadPool

Main thread pool implementation with monitoring.

Constructor:

  • ManagedThreadPool(String applicationId, ThreadPoolConfig config)

Task Submission:

  • submit(Runnable) - Submit Runnable task, returns Future<?>
  • submit(Callable<T>) - Submit Callable task, returns Future
  • execute(Runnable) - Execute Runnable without Future

Lifecycle:

  • shutdown() - Graceful shutdown (waits up to 30 seconds)
  • shutdownNow() - Force shutdown immediately
  • isShutdown() - Check if shutdown initiated
  • isTerminated() - Check if all tasks completed
  • close() - AutoCloseable support (calls shutdown)

Monitoring:

  • getStats() - Get current ThreadPoolStats snapshot
  • getApplicationId() - Get application identifier

Thread Naming: Threads are named as {applicationId}-thread-{N} for easy identification in thread dumps and monitoring tools.

ThreadPoolStats

Immutable snapshot of thread pool statistics.

Methods:

  • getActiveThreads() - Number of actively executing tasks
  • getCompletedTasks() - Total completed tasks
  • getQueuedTasks() - Number of tasks waiting in queue
  • getPoolSize() - Current number of threads in pool
  • getCorePoolSize() - Configured core pool size
  • getMaximumPoolSize() - Configured maximum pool size
  • toString() - Human-readable statistics

Use Cases

  1. Web Servers: Handle HTTP requests with bounded concurrency
  2. Background Processing: Execute async tasks with resource limits
  3. Multi-Tenant Applications: Per-tenant thread pools with isolation
  4. Batch Processing: Parallel task execution with controlled parallelism
  5. Event Processing: Asynchronous event handling

Best Practices

Sizing Thread Pools

// CPU-intensive tasks: 1 thread per core
int cpuBound = Runtime.getRuntime().availableProcessors();
ThreadPoolConfig cpuConfig = ThreadPoolConfig.builder()
    .corePoolSize(cpuBound)
    .maxPoolSize(cpuBound)
    .build();

// I/O-intensive tasks: More threads than cores
int ioBound = Runtime.getRuntime().availableProcessors() * 2;
ThreadPoolConfig ioConfig = ThreadPoolConfig.builder()
    .corePoolSize(ioBound)
    .maxPoolSize(ioBound * 2)
    .queueCapacity(1000)
    .build();

Graceful Shutdown

ManagedThreadPool pool = new ManagedThreadPool("app", config);

// Register shutdown hook
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
    System.out.println("Shutting down thread pool...");
    pool.shutdown();
}));

Exception Handling

Uncaught exceptions in tasks are automatically logged:

pool.submit(() -> {
    throw new RuntimeException("Task failed");
    // Automatically logged: [app-id] Uncaught exception in thread app-id-thread-1
});

Monitoring in Production

ScheduledExecutorService monitor = Executors.newSingleThreadScheduledExecutor();
monitor.scheduleAtFixedRate(() -> {
    ThreadPoolStats stats = pool.getStats();
    logger.info("Pool stats: {}", stats);
    
    // Alert if queue is growing
    if (stats.getQueuedTasks() > 80) {
        logger.warn("Thread pool queue filling up!");
    }
}, 0, 30, TimeUnit.SECONDS);

Design Principles

  • Bounded Resources: Configurable limits prevent resource exhaustion
  • Fail-Safe: CallerRunsPolicy prevents task rejection
  • Observable: Real-time statistics for monitoring
  • Lifecycle Management: Clear shutdown semantics
  • Thread Safety: All operations are thread-safe

Requirements

  • Java 21 or higher
  • SLF4J API (for logging)

License

MIT License - see LICENSE file for details.

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

Links

Changelog

See CHANGELOG.md for version history.