Skip to content

gemacjr/spring-boot-concurrency

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

3 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Spring Boot Concurrency Patterns Demo

A comprehensive demonstration of concurrency patterns in Spring Boot, showcasing best practices for building high-performance, concurrent applications.

πŸ“‹ Table of Contents

🎯 Overview

This application demonstrates various concurrency patterns and best practices in Java and Spring Boot, including:

  • Asynchronous programming with @Async
  • CompletableFuture for composable async operations
  • Parallel Streams for data processing
  • Thread pools with ExecutorService
  • Locks for fine-grained synchronization
  • Synchronization primitives (CountDownLatch, CyclicBarrier, Semaphore, Phaser)
  • Lock-free programming with Atomic classes
  • Producer-Consumer with BlockingQueue
  • Fork/Join framework for divide-and-conquer algorithms
  • Scheduled tasks with @Scheduled

πŸ›  Technologies

  • Java 17
  • Spring Boot 3.5.7
  • Spring Web - REST API
  • Spring Data JPA - Database access
  • H2 Database - In-memory database
  • Lombok - Reduce boilerplate code
  • JUnit 5 - Unit testing
  • Awaitility - Async testing
  • Spring Boot Actuator - Monitoring

πŸ”„ Concurrency Patterns

1. @Async Annotation

Spring's @Async provides simple asynchronous method execution with custom thread pools.

Service: AsyncService

Features:

  • Fire-and-forget async methods
  • Async methods with return values (CompletableFuture)
  • Multiple thread pool configurations (default, I/O, CPU, long-running)
  • Proper exception handling

Example:

@Async("ioTaskExecutor")
public CompletableFuture<TaskResult<String>> executeIoTask(ProcessingTask task) {
    // I/O bound operation
}

Use Cases:

  • Non-blocking I/O operations
  • Background processing
  • Email sending
  • External API calls

2. CompletableFuture

Advanced asynchronous programming with composable futures.

Service: CompletableFutureService

Features:

  • Chaining operations (thenApply, thenCompose)
  • Combining multiple futures (thenCombine, allOf, anyOf)
  • Exception handling (exceptionally, handle)
  • Timeout support

Example:

CompletableFuture.supplyAsync(() -> fetchData())
    .thenApply(data -> process(data))
    .thenApply(result -> transform(result))
    .exceptionally(ex -> handleError(ex));

Use Cases:

  • Complex async workflows
  • Dependent operations
  • Parallel data fetching
  • Timeout-sensitive operations

3. Parallel Streams

Declarative parallel processing of collections.

Service: ParallelStreamsService

Features:

  • Automatic parallelization
  • Filter, map, reduce operations
  • Custom ForkJoinPool parallelism
  • Performance comparison with sequential

Example:

List<Integer> results = numbers.parallelStream()
    .filter(n -> n > 0)
    .map(n -> n * n)
    .collect(Collectors.toList());

Use Cases:

  • CPU-intensive operations on large datasets
  • Bulk data transformations
  • Statistical computations

4. ExecutorService

Low-level thread pool management.

Service: ExecutorServicePatternService

Features:

  • Fixed thread pool
  • Cached thread pool
  • Scheduled executor
  • Work-stealing pool
  • invokeAll/invokeAny patterns

Example:

ExecutorService executor = Executors.newFixedThreadPool(10);
Future<Result> future = executor.submit(() -> longRunningTask());
Result result = future.get(5, TimeUnit.SECONDS);

Use Cases:

  • Fine-grained thread pool control
  • Task submission with timeouts
  • Batch processing
  • Custom scheduling

5. Locks

Explicit locking for synchronization control.

Service: LocksService

Features:

  • ReentrantLock
  • ReadWriteLock
  • tryLock with timeout
  • Fair/unfair locks

Example:

ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
    // Critical section
} finally {
    lock.unlock();
}

Use Cases:

  • Complex synchronization logic
  • Read-heavy workloads (ReadWriteLock)
  • Deadlock avoidance (tryLock)

6. Synchronization Primitives

High-level coordination mechanisms.

Service: SynchronizationService

Features:

  • CountDownLatch - One-time countdown coordination
  • CyclicBarrier - Reusable barrier for phase coordination
  • Semaphore - Resource access control
  • Phaser - Dynamic phase coordination

Example:

CountDownLatch latch = new CountDownLatch(5);
// ... submit 5 tasks, each calls latch.countDown()
latch.await(); // Wait for all tasks

Use Cases:

  • Waiting for multiple tasks to complete
  • Phase-based parallel algorithms
  • Resource pooling
  • Rate limiting

7. Atomic Classes

Lock-free thread-safe operations.

Service: AtomicOperationsService

Features:

  • AtomicInteger, AtomicLong, AtomicBoolean, AtomicReference
  • Compare-and-swap (CAS) operations
  • LongAdder for high-contention scenarios
  • LongAccumulator for custom accumulation

Example:

AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet(); // Thread-safe increment
counter.compareAndSet(expected, newValue); // CAS

Use Cases:

  • Counters and metrics
  • Lock-free data structures
  • High-contention updates

8. BlockingQueue (Producer-Consumer)

Thread-safe queues for producer-consumer patterns.

Service: BlockingQueueService

Features:

  • ArrayBlockingQueue (bounded)
  • LinkedBlockingQueue (unbounded)
  • PriorityBlockingQueue
  • DelayQueue
  • SynchronousQueue

Example:

BlockingQueue<Task> queue = new ArrayBlockingQueue<>(10);
queue.put(task); // Producer
Task task = queue.take(); // Consumer (blocks if empty)

Use Cases:

  • Task queues
  • Message passing
  • Work distribution
  • Buffering

9. Fork/Join Framework

Recursive parallel algorithms.

Service: ForkJoinService

Features:

  • RecursiveTask (returns result)
  • RecursiveAction (no result)
  • Work-stealing
  • Divide-and-conquer

Example:

class SumTask extends RecursiveTask<Long> {
    protected Long compute() {
        if (small enough) return directSum();
        SumTask left = new SumTask(leftHalf);
        SumTask right = new SumTask(rightHalf);
        left.fork();
        return right.compute() + left.join();
    }
}

Use Cases:

  • Parallel sorting
  • Recursive algorithms
  • Tree/graph processing
  • Large array operations

10. @Scheduled Tasks

Cron-like task scheduling.

Service: ScheduledTasksService

Features:

  • Fixed rate execution
  • Fixed delay execution
  • Cron expressions
  • Exception handling

Example:

@Scheduled(fixedRate = 5000)
public void periodicTask() {
    // Runs every 5 seconds
}

@Scheduled(cron = "0 0 * * * *")
public void hourlyTask() {
    // Runs every hour
}

Use Cases:

  • Periodic maintenance
  • Batch processing
  • Data synchronization
  • Health checks

πŸš€ Getting Started

Prerequisites

  • Java 17 or higher
  • Gradle 7+

Build and Run

# Build the project
./gradlew build

# Run the application
./gradlew bootRun

# Run tests
./gradlew test

The application will start on http://localhost:8080

Access H2 Console

Navigate to http://localhost:8080/h2-console

  • JDBC URL: jdbc:h2:mem:testdb
  • Username: sa
  • Password: (empty)

πŸ“‘ API Endpoints

Health & Monitoring

GET /api/concurrency/health
GET /actuator/health
GET /actuator/metrics
GET /actuator/threaddump

Async Service

POST /api/concurrency/async/simple?taskName=MyTask
POST /api/concurrency/async/with-return?taskName=MyTask
POST /api/concurrency/async/io-task
POST /api/concurrency/async/cpu-task

CompletableFuture

POST /api/concurrency/completable-future/simple?taskName=MyTask
POST /api/concurrency/completable-future/chained?input=test
POST /api/concurrency/completable-future/combined?task1=A&task2=B
POST /api/concurrency/completable-future/all
POST /api/concurrency/completable-future/any

Parallel Streams

POST /api/concurrency/parallel-streams/numbers
POST /api/concurrency/parallel-streams/sum
POST /api/concurrency/parallel-streams/factorials?upTo=10
POST /api/concurrency/parallel-streams/compare?count=1000

Locks

POST /api/concurrency/locks/reentrant?times=100
POST /api/concurrency/locks/try-lock?timeoutMs=1000
POST /api/concurrency/locks/read-write?readers=5&writers=2
POST /api/concurrency/locks/reset

Synchronization

POST /api/concurrency/sync/countdown-latch?numberOfTasks=5
POST /api/concurrency/sync/cyclic-barrier?numberOfThreads=3&cycles=2
POST /api/concurrency/sync/semaphore?permits=3&requesters=10
POST /api/concurrency/sync/phaser?numberOfParties=4&phases=3

Atomic Operations

POST /api/concurrency/atomic/integer?increments=100
POST /api/concurrency/atomic/cas?expectedValue=10&newValue=20
POST /api/concurrency/atomic/long-adder?increments=1000
POST /api/concurrency/atomic/compare?iterations=1000&threads=10
POST /api/concurrency/atomic/reset

BlockingQueue

POST /api/concurrency/queue/producer-consumer?producers=2&consumers=3&itemsPerProducer=10
POST /api/concurrency/queue/priority?items=10
POST /api/concurrency/queue/delay?items=5

Fork/Join

POST /api/concurrency/fork-join/sum
POST /api/concurrency/fork-join/square
POST /api/concurrency/fork-join/sort
POST /api/concurrency/fork-join/max

Scheduled Tasks

GET /api/concurrency/scheduled/counts
POST /api/concurrency/scheduled/reset

Utilities

GET /api/concurrency/demo/tasks?count=10

πŸ§ͺ Testing

The project includes comprehensive tests:

  • Unit Tests - Test individual services
  • Integration Tests - Test REST controllers
  • Async Testing - Using Awaitility
# Run all tests
./gradlew test

# Run specific test class
./gradlew test --tests AsyncServiceTest

# Run tests with coverage
./gradlew test jacocoTestReport

βœ… Best Practices

1. Thread Pool Configuration

  • I/O Bound Tasks: Large pool (threads > CPU cores)
  • CPU Bound Tasks: Pool size β‰ˆ CPU cores
  • Mixed Workload: Separate pools for different task types

2. Exception Handling

Always handle exceptions in async methods:

@Async
public CompletableFuture<Result> asyncMethod() {
    try {
        return CompletableFuture.completedFuture(result);
    } catch (Exception e) {
        return CompletableFuture.completedFuture(errorResult);
    }
}

3. Resource Cleanup

Always shutdown executors:

executor.shutdown();
executor.awaitTermination(60, TimeUnit.SECONDS);

4. Lock Management

Always use try-finally with locks:

lock.lock();
try {
    // Critical section
} finally {
    lock.unlock();
}

5. Avoid Deadlocks

  • Acquire locks in consistent order
  • Use tryLock with timeout
  • Keep critical sections small

6. Parallel Streams Considerations

  • Not suitable for I/O operations
  • Overhead for small datasets
  • Be aware of shared state

7. Atomic vs Synchronized

  • Use atomics for simple operations
  • Use locks for complex operations
  • Consider LongAdder for high contention

πŸ“Š Performance Considerations

Thread Pool Sizing

CPU-bound: threads = cores + 1
I/O-bound: threads = cores * (1 + wait_time/compute_time)

Parallel Stream Threshold

Use parallel streams when:

  • Dataset size > 1000 elements
  • Operations are CPU-intensive
  • No I/O operations involved

Lock Contention

Minimize lock contention by:

  • Reducing critical section size
  • Using ReadWriteLock for read-heavy workloads
  • Consider lock-free alternatives (atomics)

πŸ“š Additional Resources

πŸ“ License

This project is for educational purposes.

πŸ‘€ Author

Spring Boot Concurrency Demo - Best Practices Implementation


Note: This application is designed for learning and demonstration purposes. Always benchmark and test thoroughly for production use.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages