Claude-skill-registry java-concurrency
Use when Java concurrency with ExecutorService, CompletableFuture, and virtual threads. Use when building concurrent applications.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/java-concurrency" ~/.claude/skills/majiayu000-claude-skill-registry-java-concurrency && rm -rf "$T"
skills/data/java-concurrency/SKILL.mdJava Concurrency
Master Java's concurrency utilities including ExecutorService, CompletableFuture, locks, and modern virtual threads for building high-performance concurrent applications.
Thread Basics
Understanding Java threads is fundamental to concurrency.
Creating and running threads:
public class ThreadBasics { public static void main(String[] args) { // Using Thread class Thread thread1 = new Thread(() -> { System.out.println("Running in thread: " + Thread.currentThread().getName()); }); thread1.start(); // Using Runnable Runnable task = () -> { for (int i = 0; i < 5; i++) { System.out.println("Task iteration: " + i); } }; Thread thread2 = new Thread(task); thread2.start(); // Join threads try { thread1.join(); thread2.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }
ExecutorService
ExecutorService provides thread pool management and task scheduling.
Basic executor usage:
import java.util.concurrent.*; public class ExecutorBasics { public static void main(String[] args) { // Fixed thread pool ExecutorService executor = Executors.newFixedThreadPool(3); // Submit tasks for (int i = 0; i < 5; i++) { final int taskId = i; executor.submit(() -> { System.out.println("Task " + taskId + " on " + Thread.currentThread().getName()); return taskId * 2; }); } // Shutdown executor executor.shutdown(); try { if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { executor.shutdownNow(); } } catch (InterruptedException e) { executor.shutdownNow(); Thread.currentThread().interrupt(); } } }
Different executor types:
public class ExecutorTypes { public static void main(String[] args) { // Single thread executor ExecutorService single = Executors.newSingleThreadExecutor(); // Fixed thread pool ExecutorService fixed = Executors.newFixedThreadPool(4); // Cached thread pool (creates threads as needed) ExecutorService cached = Executors.newCachedThreadPool(); // Scheduled executor ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(2); // Schedule task with delay scheduled.schedule(() -> { System.out.println("Delayed task"); }, 5, TimeUnit.SECONDS); // Schedule periodic task scheduled.scheduleAtFixedRate(() -> { System.out.println("Periodic task"); }, 0, 1, TimeUnit.SECONDS); // Work stealing pool (uses available processors) ExecutorService workStealing = Executors.newWorkStealingPool(); } }
Future pattern:
public class FutureExample { public static void main(String[] args) throws Exception { ExecutorService executor = Executors.newFixedThreadPool(2); // Submit callable Future<Integer> future = executor.submit(() -> { Thread.sleep(1000); return 42; }); // Do other work System.out.println("Waiting for result..."); // Get result (blocks until ready) Integer result = future.get(); System.out.println("Result: " + result); // With timeout try { Integer result2 = future.get(500, TimeUnit.MILLISECONDS); } catch (TimeoutException e) { System.out.println("Timed out"); future.cancel(true); } // Check status boolean isDone = future.isDone(); boolean isCancelled = future.isCancelled(); executor.shutdown(); } }
CompletableFuture
CompletableFuture enables composable asynchronous programming.
Basic CompletableFuture:
import java.util.concurrent.CompletableFuture; public class CompletableFutureBasics { public static void main(String[] args) { // Create completed future CompletableFuture<String> future = CompletableFuture.completedFuture("Hello"); // Async computation CompletableFuture<Integer> asyncFuture = CompletableFuture.supplyAsync(() -> { sleep(1000); return 42; }); // Run async without return value CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> { System.out.println("Running async"); }); // Get result (blocking) try { Integer result = asyncFuture.get(); System.out.println("Result: " + result); } catch (Exception e) { e.printStackTrace(); } } private static void sleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }
Chaining operations:
public class CompletableFutureChaining { public static void main(String[] args) { // thenApply - transform result CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello") .thenApply(s -> s + " World") .thenApply(String::toUpperCase); System.out.println(future.join()); // HELLO WORLD // thenAccept - consume result CompletableFuture.supplyAsync(() -> 42) .thenAccept(result -> System.out.println("Result: " + result)); // thenRun - run after completion CompletableFuture.supplyAsync(() -> "Done") .thenRun(() -> System.out.println("Finished")); // thenCompose - flatten nested futures CompletableFuture<String> composed = CompletableFuture.supplyAsync(() -> "User123") .thenCompose(userId -> fetchUserDetails(userId)); } static CompletableFuture<String> fetchUserDetails(String userId) { return CompletableFuture.supplyAsync(() -> "Details for " + userId); } }
Combining futures:
public class CombiningFutures { public static void main(String[] args) { CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 10); CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 20); // Combine two futures CompletableFuture<Integer> combined = future1.thenCombine( future2, (a, b) -> a + b ); System.out.println(combined.join()); // 30 // Accept both results future1.thenAcceptBoth(future2, (a, b) -> System.out.println("Sum: " + (a + b))); // Run after both complete future1.runAfterBoth(future2, () -> System.out.println("Both completed")); // Either - whichever completes first CompletableFuture<String> either = future1.applyToEither(future2, result -> "First result: " + result); // All of - wait for all CompletableFuture<Void> allOf = CompletableFuture.allOf(future1, future2); // Any of - wait for any CompletableFuture<Object> anyOf = CompletableFuture.anyOf(future1, future2); } }
Error handling:
public class FutureErrorHandling { public static void main(String[] args) { // exceptionally - handle error CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> { if (Math.random() > 0.5) { throw new RuntimeException("Error!"); } return 42; }).exceptionally(ex -> { System.err.println("Error: " + ex.getMessage()); return -1; // Default value }); // handle - handle both success and error CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 10 / 0) .handle((result, ex) -> { if (ex != null) { System.err.println("Error: " + ex.getMessage()); return 0; } return result; }); // whenComplete - side effect for both success and error CompletableFuture.supplyAsync(() -> "Hello") .whenComplete((result, ex) -> { if (ex != null) { System.err.println("Failed"); } else { System.out.println("Success: " + result); } }); } }
Locks and Synchronization
Beyond synchronized blocks, Java provides explicit locks.
ReentrantLock:
import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockExample { private final ReentrantLock lock = new ReentrantLock(); private int counter = 0; public void increment() { lock.lock(); try { counter++; } finally { lock.unlock(); // Always unlock in finally } } public void tryLockExample() { if (lock.tryLock()) { try { // Critical section counter++; } finally { lock.unlock(); } } else { System.out.println("Could not acquire lock"); } } public void fairLockExample() { // Fair lock - serves threads in order ReentrantLock fairLock = new ReentrantLock(true); fairLock.lock(); try { // Critical section } finally { fairLock.unlock(); } } }
ReadWriteLock:
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.HashMap; import java.util.Map; public class ReadWriteLockExample { private final Map<String, String> cache = new HashMap<>(); private final ReadWriteLock lock = new ReentrantReadWriteLock(); public String get(String key) { lock.readLock().lock(); try { return cache.get(key); } finally { lock.readLock().unlock(); } } public void put(String key, String value) { lock.writeLock().lock(); try { cache.put(key, value); } finally { lock.writeLock().unlock(); } } }
Condition variables:
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.LinkedList; import java.util.Queue; public class BoundedBuffer<T> { private final Queue<T> queue = new LinkedList<>(); private final int capacity; private final Lock lock = new ReentrantLock(); private final Condition notFull = lock.newCondition(); private final Condition notEmpty = lock.newCondition(); public BoundedBuffer(int capacity) { this.capacity = capacity; } public void put(T item) throws InterruptedException { lock.lock(); try { while (queue.size() == capacity) { notFull.await(); // Wait until not full } queue.add(item); notEmpty.signal(); // Signal waiting consumers } finally { lock.unlock(); } } public T take() throws InterruptedException { lock.lock(); try { while (queue.isEmpty()) { notEmpty.await(); // Wait until not empty } T item = queue.remove(); notFull.signal(); // Signal waiting producers return item; } finally { lock.unlock(); } } }
CountDownLatch and CyclicBarrier
Coordination utilities for managing thread synchronization.
CountDownLatch:
import java.util.concurrent.CountDownLatch; public class CountDownLatchExample { public static void main(String[] args) throws InterruptedException { int workerCount = 3; CountDownLatch startSignal = new CountDownLatch(1); CountDownLatch doneSignal = new CountDownLatch(workerCount); for (int i = 0; i < workerCount; i++) { new Thread(new Worker(startSignal, doneSignal)).start(); } System.out.println("Preparing workers..."); Thread.sleep(1000); startSignal.countDown(); // Start all workers doneSignal.await(); // Wait for all to finish System.out.println("All workers completed"); } static class Worker implements Runnable { private final CountDownLatch startSignal; private final CountDownLatch doneSignal; Worker(CountDownLatch startSignal, CountDownLatch doneSignal) { this.startSignal = startSignal; this.doneSignal = doneSignal; } public void run() { try { startSignal.await(); // Wait for start signal doWork(); doneSignal.countDown(); // Signal completion } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } void doWork() { System.out.println("Worker " + Thread.currentThread().getName() + " working"); } } }
CyclicBarrier:
import java.util.concurrent.CyclicBarrier; import java.util.concurrent.BrokenBarrierException; public class CyclicBarrierExample { public static void main(String[] args) { int parties = 3; CyclicBarrier barrier = new CyclicBarrier(parties, () -> { System.out.println("All threads reached barrier"); }); for (int i = 0; i < parties; i++) { new Thread(new Task(barrier, i)).start(); } } static class Task implements Runnable { private final CyclicBarrier barrier; private final int id; Task(CyclicBarrier barrier, int id) { this.barrier = barrier; this.id = id; } public void run() { try { System.out.println("Task " + id + " working"); Thread.sleep(1000 * id); System.out.println("Task " + id + " waiting at barrier"); barrier.await(); // Wait for others System.out.println("Task " + id + " passed barrier"); } catch (InterruptedException | BrokenBarrierException e) { Thread.currentThread().interrupt(); } } } }
Virtual Threads
Virtual threads enable lightweight concurrency at scale.
Creating virtual threads:
public class VirtualThreads { public static void main(String[] args) throws InterruptedException { // Create and start virtual thread Thread vThread = Thread.startVirtualThread(() -> { System.out.println("Running in virtual thread: " + Thread.currentThread()); }); vThread.join(); // Using builder Thread virtual = Thread.ofVirtual() .name("virtual-worker") .start(() -> { System.out.println("Virtual thread task"); }); virtual.join(); // Factory for virtual threads ThreadFactory factory = Thread.ofVirtual().factory(); Thread t = factory.newThread(() -> { System.out.println("Created by factory"); }); t.start(); t.join(); } }
Virtual thread executor:
import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; public class VirtualThreadExecutor { public static void main(String[] args) { // Executor with virtual threads try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) { // Submit many tasks for (int i = 0; i < 10_000; i++) { final int taskId = i; executor.submit(() -> { Thread.sleep(1000); System.out.println("Task " + taskId); return null; }); } } // Auto-shutdown with try-with-resources } }
Atomic Variables
Lock-free thread-safe operations using atomic classes.
AtomicInteger and friends:
import java.util.concurrent.atomic.*; public class AtomicExample { private final AtomicInteger counter = new AtomicInteger(0); private final AtomicLong longCounter = new AtomicLong(0); private final AtomicBoolean flag = new AtomicBoolean(false); private final AtomicReference<String> ref = new AtomicReference<>("initial"); public void increment() { counter.incrementAndGet(); } public int getAndAdd(int delta) { return counter.getAndAdd(delta); } public boolean compareAndSet(int expect, int update) { return counter.compareAndSet(expect, update); } public void updateReference() { ref.updateAndGet(current -> current.toUpperCase()); } public void accumulateExample() { // Accumulate with custom operation counter.accumulateAndGet(5, (current, value) -> current + value * 2); } }
Thread-Safe Collections
Concurrent collections for safe multi-threaded access.
ConcurrentHashMap:
import java.util.concurrent.ConcurrentHashMap; public class ConcurrentMapExample { private final ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(); public void basicOperations() { // Thread-safe put map.put("key", 1); // Atomic put if absent map.putIfAbsent("key", 2); // Won't update, returns 1 // Atomic compute map.compute("key", (k, v) -> v == null ? 1 : v + 1); // Atomic compute if absent map.computeIfAbsent("newKey", k -> k.length()); // Atomic compute if present map.computeIfPresent("key", (k, v) -> v * 2); // Atomic replace map.replace("key", 1, 10); // Only if current value is 1 // Merge values map.merge("key", 5, (oldVal, newVal) -> oldVal + newVal); } public void bulkOperations() { // forEach with parallelism threshold map.forEach(10, (k, v) -> System.out.println(k + " = " + v)); // Search String result = map.search(10, (k, v) -> v > 100 ? k : null); // Reduce Integer sum = map.reduce(10, (k, v) -> v, (v1, v2) -> v1 + v2); } }
Other concurrent collections:
import java.util.concurrent.*; public class ConcurrentCollections { public static void main(String[] args) { // Blocking queue BlockingQueue<String> queue = new LinkedBlockingQueue<>(10); // Priority blocking queue BlockingQueue<Integer> priorityQueue = new PriorityBlockingQueue<>(); // Concurrent linked queue (non-blocking) ConcurrentLinkedQueue<String> linkedQueue = new ConcurrentLinkedQueue<>(); // Copy on write list (reads without locking) CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(); // Concurrent skip list map (sorted) ConcurrentSkipListMap<String, Integer> skipList = new ConcurrentSkipListMap<>(); } }
When to Use This Skill
Use java-concurrency when you need to:
- Execute tasks concurrently with thread pools
- Perform asynchronous operations with callbacks
- Coordinate multiple threads with barriers or latches
- Implement producer-consumer patterns
- Handle high-concurrency scenarios with virtual threads
- Protect shared state with locks or atomic operations
- Process tasks in parallel for better performance
- Implement timeout and cancellation for long operations
- Build reactive or event-driven applications
- Scale applications to handle thousands of concurrent tasks
Best Practices
- Use ExecutorService instead of raw threads
- Always shutdown executors properly
- Prefer CompletableFuture for async operations
- Use virtual threads for I/O-bound tasks
- Minimize lock contention and critical sections
- Use concurrent collections over synchronized collections
- Handle InterruptedException appropriately
- Avoid blocking operations in CompletableFuture chains
- Use try-finally for lock acquisition/release
- Consider using atomic variables over locks
Common Pitfalls
- Not shutting down executors (resource leak)
- Blocking virtual threads with synchronized blocks
- Deadlocks from incorrect lock ordering
- Race conditions from improper synchronization
- Thread pool exhaustion from blocking tasks
- Ignoring InterruptedException
- Using too many platform threads (use virtual threads)
- Not handling CompletableFuture exceptions
- Excessive lock contention hurting performance
- Incorrect use of volatile vs atomic vs synchronized