AtomicIntegre and CountDownLatch
Advanced Examples Using AtomicInteger and CountDownLatch
Below are examples of how AtomicInteger and CountDownLatch can be used in more advanced scenarios:
1. AtomicInteger for a Multi-Threaded Counter with Custom Logic
AtomicInteger is often used to handle counters in a thread-safe manner. Here’s an example where it is used to count tasks completed by multiple threads and apply some custom logic.
Scenario: Count Tasks with Custom Increment Logic
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerExample {
public static void main(String[] args) {
AtomicInteger counter = new AtomicInteger(0);
ExecutorService executor = Executors.newFixedThreadPool(5);
Runnable task = () -> {
int increment = (int) (Math.random() * 10); // Custom increment
int updatedValue = counter.addAndGet(increment);
System.out.println(Thread.currentThread().getName() + " incremented by " + increment + ", total: " + updatedValue);
};
// Submit 10 tasks
for (int i = 0; i < 10; i++) {
executor.submit(task);
}
executor.shutdown();
}
}
Key Points:
• addAndGet(int delta) ensures atomic addition to the counter.
• Each thread performs a custom increment without causing race conditions.
2. CountdownLatch for Coordinating Multiple Threads
CountDownLatch can be used to wait for a set of threads to complete tasks before proceeding.
Scenario: Waiting for Multiple Threads to Complete a Preprocessing Step
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
int numberOfTasks = 5;
CountDownLatch latch = new CountDownLatch(numberOfTasks);
Runnable worker = () -> {
try {
System.out.println(Thread.currentThread().getName() + " is processing...");
Thread.sleep((long) (Math.random() * 3000)); // Simulate work
System.out.println(Thread.currentThread().getName() + " finished processing.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
latch.countDown(); // Reduce the latch count
}
};
// Start worker threads
for (int i = 0; i < numberOfTasks; i++) {
new Thread(worker).start();
}
System.out.println("Main thread is waiting for workers to finish...");
latch.await(); // Wait until the latch count reaches zero
System.out.println("All workers have finished. Main thread resumes.");
}
}
Key Points:
• latch.countDown() reduces the latch count by 1 each time a worker finishes.
• latch.await() blocks the main thread until all workers finish their tasks.
3. Using AtomicInteger with CountdownLatch for Task Tracking
Combine AtomicInteger and CountDownLatch to track how many threads successfully completed a task.
Scenario: Track Successful Task Completions
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerWithLatchExample {
public static void main(String[] args) throws InterruptedException {
int numberOfThreads = 5;
CountDownLatch latch = new CountDownLatch(numberOfThreads);
AtomicInteger successCounter = new AtomicInteger(0);
Runnable task = () -> {
try {
System.out.println(Thread.currentThread().getName() + " is working...");
if (Math.random() > 0.3) { // Simulate a success with 70% probability
successCounter.incrementAndGet();
}
Thread.sleep((long) (Math.random() * 2000));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
latch.countDown(); // Decrease latch count
}
};
for (int i = 0; i < numberOfThreads; i++) {
new Thread(task).start();
}
latch.await(); // Wait for all threads to finish
System.out.println("All threads finished. Successful completions: " + successCounter.get());
}
}
Key Points:
• AtomicInteger is used to count successful completions in a thread-safe way.
• CountDownLatch ensures the main thread waits for all tasks to complete.
4. Parallel Processing with CountDownLatch
CountDownLatch can be used to implement parallel processing where multiple tasks must start simultaneously.
Scenario: Launch All Threads Simultaneously
import java.util.concurrent.CountDownLatch;
public class ParallelProcessingExample {
public static void main(String[] args) throws InterruptedException {
int numberOfThreads = 5;
CountDownLatch startLatch = new CountDownLatch(1); // Wait for the signal to start
CountDownLatch finishLatch = new CountDownLatch(numberOfThreads); // Wait for all threads to finish
Runnable task = () -> {
try {
startLatch.await(); // Wait for the signal
System.out.println(Thread.currentThread().getName() + " started working...");
Thread.sleep((long) (Math.random() * 3000)); // Simulate work
System.out.println(Thread.currentThread().getName() + " finished.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
finishLatch.countDown(); // Signal completion
}
};
for (int i = 0; i < numberOfThreads; i++) {
new Thread(task).start();
}
System.out.println("Ready... Set...");
startLatch.countDown(); // Signal all threads to start
System.out.println("Go!");
finishLatch.await(); // Wait for all threads to finish
System.out.println("All threads have completed their work.");
}
}
Key Points:
• startLatch ensures all threads start simultaneously.
• finishLatch ensures the main thread waits for all threads to complete.
5. Limiting Access with AtomicInteger
Use AtomicInteger to simulate a rate limiter where only a fixed number of threads are allowed to access a resource concurrently.
Scenario: Simulating a Rate Limiter
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class RateLimiterExample {
public static void main(String[] args) {
AtomicInteger activeThreads = new AtomicInteger(0);
int maxThreads = 3;
ExecutorService executor = Executors.newFixedThreadPool(5);
Runnable task = () -> {
if (activeThreads.incrementAndGet() <= maxThreads) {
try {
System.out.println(Thread.currentThread().getName() + " is working...");
Thread.sleep((long) (Math.random() * 2000)); // Simulate work
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
activeThreads.decrementAndGet();
}
} else {
System.out.println(Thread.currentThread().getName() + " is denied access (rate limit exceeded).");
activeThreads.decrementAndGet(); // Decrease count if denied
}
};
for (int i = 0; i < 10; i++) {
executor.submit(task);
}
executor.shutdown();
}
}
Key Points:
• AtomicInteger tracks the number of active threads.
• Threads exceeding the rate limit are denied access.
These examples cover various practical use cases for AtomicInteger and CountDownLatch, showcasing how they can be used in advanced multithreading scenarios to solve real-world problems effectively.
0