JavaSE-10-并发编程(11个案例)
本文将通过多个高并发场景下的实战案例,深入学习理解线程池、线程协作、线程安全集合、锁机制、ThreadLocal 等并发工具的综合运用,进一步巩固并发编程技能,提升实战能力。
1. 生产者-消费者
1.1 场景描述
生产者-消费者模型是并发编程中最经典的模型之一,用于解耦数据的生产与消费,适用于任务队列、消息队列等场景。
1.2 实现方式
import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; public class ProducerConsumer { private final BlockingQueue<Integer> queue = new LinkedBlockingQueue <>( 10 ); class Producer implements Runnable { @Override public void run () { try { for ( int i = 0 ; i < 100 ; i++) { queue.put(i); System.out.println( "Produced: " + i); Thread.sleep( 50 ); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } class Consumer implements Runnable { @Override public void run () { try { while ( true ) { Integer value = queue.take(); System.out.println( "Consumed: " + value); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } public static void main (String[] args) { ProducerConsumer pc = new ProducerConsumer (); new Thread (pc. new Producer ()).start(); new Thread (pc. new Consumer ()).start(); } }
1.3 关键点分析
| 组件 | 作用 |
|---|---|
| BlockingQueue | 提供线程安全的队列操作(put/take) |
| LinkedBlockingQueue | 支持无界队列,适合高吞吐场景 |
| Thread.sleep() | 模拟生产/消费耗时 |
| InterruptedException | 捕获中断信号,保证线程安全退出 |
1.4 优化建议
- 使用线程池管理生产者/消费者线程;
- 使用
ArrayBlockingQueue限制队列大小,防止内存溢出; - 使用
SynchronousQueue实现直接传递任务; - 使用
DelayQueue实现延迟消费逻辑。
2. 异步任务处理
2.1 场景描述
在高并发系统中,经常需要并行执行多个任务并等待结果,如并发调用多个接口、批量处理任务等。可以使用线程池 + Future 实现异步处理。
2.2 实现方式
import java.util.concurrent.*; public class FutureTaskExample { public static void main (String[] args) throws ExecutionException, InterruptedException { ExecutorService executor = Executors.newFixedThreadPool( 4 ); Future<Integer> future1 = executor.submit(() -> { Thread.sleep( 1000 ); return 100 ; }); Future<Integer> future2 = executor.submit(() -> { Thread.sleep( 1500 ); return 200 ; }); System.out.println( "Result1: " + future1.get()); System.out.println( "Result2: " + future2.get()); executor.shutdown(); } }
2.3 关键点分析
| 组件 | 作用 |
|---|---|
| ExecutorService | 线程池管理任务执行 |
| Future | 异步获取任务结果 |
| get() | 阻塞等待任务完成 |
| submit(Callable) | 提交带返回值的任务 |
| shutdown() | 平滑关闭线程池 |
2.4 优化建议
- 使用
invokeAll()批量提交任务; - 使用
CompletableFuture替代Future,支持链式调用; - 使用
Future.get(timeout, unit)避免任务长时间阻塞; - 使用
ThreadPoolTaskExecutor(Spring)进行更精细控制;
3. 自定义阻塞队列
3.1 场景描述
当需要实现一个自定义阻塞队列 (如用于任务调度、事件驱动系统),可以使用 ReentrantLock 和 Condition 实现。
3.2 实现方式
import java.util.LinkedList; import java.util.Queue; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class CustomBlockingQueue <T> { private final Queue<T> queue = new LinkedList <>(); private final int capacity; private final Lock lock = new ReentrantLock (); private final Condition notEmpty = lock.newCondition(); private final Condition notFull = lock.newCondition(); public CustomBlockingQueue ( int capacity) { this .capacity = capacity; } public void put (T item) throws InterruptedException { lock.lock(); try { while (queue.size() == capacity) { notFull.await(); } queue.offer(item); notEmpty.signal(); } finally { lock.unlock(); } } public T take () throws InterruptedException { lock.lock(); try { while (queue.isEmpty()) { notEmpty.await(); } T item = queue.poll(); notFull.signal(); return item; } finally { lock.unlock(); } } public static void main (String[] args) { CustomBlockingQueue<Integer> queue = new CustomBlockingQueue <>( 5 ); new Thread (() -> { try { for ( int i = 0 ; i < 10 ; i++) { queue.put(i); System.out.println( "Put: " + i); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }).start(); new Thread (() -> { try { for ( int i = 0 ; i < 10 ; i++) { System.out.println( "Take: " + queue.take()); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }).start(); } }
3.3 关键点分析
| 组件 | 作用 |
|---|---|
| ReentrantLock | 提供可中断、公平锁等高级锁控制 |
| Condition | 替代传统的 wait/notify ,实现更清晰的线程等待/唤醒机制 |
| await() / signal() | 等待和唤醒线程 |
| while 检查条件 | 避免虚假唤醒 |
3.4 优化建议
- 使用
ReentrantLock的公平锁模式控制线程调度顺序; - 使用
tryLock()避免死锁; - 使用
Condition替代synchronized和wait/notify,提高可读性和灵活性; - 可扩展为
ArrayBlockingQueue、LinkedBlockingQueue的简化版实现;
4. 多线程下载文件并分块合并
4.1 场景描述
在下载大文件时,使用多线程分段下载,并最终合并文件片段,提高下载效率。
4.2 实现思路
-
- 计算文件总大小;
-
- 根据线程数划分下载区间;
-
- 使用线程池并发下载各段;
-
- 合并所有片段;
-
- 使用
CountDownLatch控制合并时机;
- 使用
4.3 示例代码
import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.util.concurrent.*; public class MultiThreadDownloader { private static final int THREAD_COUNT = 4 ; public static void main (String[] args) throws Exception { String urlStr = "http://example.com/largefile.zip" ; URL url = new URL (urlStr); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); int fileSize = conn.getContentLength(); int partSize = fileSize / THREAD_COUNT; CountDownLatch latch = new CountDownLatch (THREAD_COUNT); ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT); for ( int i = 0 ; i < THREAD_COUNT; i++) { final int index = i; final int start = i * partSize; final int end = (i == THREAD_COUNT - 1 ) ? fileSize : (i + 1 ) * partSize - 1 ; executor.submit(() -> { try { downloadPart(urlStr, start, end, index); latch.countDown(); } catch (Exception e) { e.printStackTrace(); } }); } latch.await(); executor.shutdown(); mergeFiles(THREAD_COUNT); } private static void downloadPart (String urlStr, int start, int end, int index) throws Exception { HttpURLConnection conn = (HttpURLConnection) new URL (urlStr).openConnection(); conn.setRequestProperty( "Range" , "bytes=" + start + "-" + end); try ( InputStream in = conn.getInputStream(); FileOutputStream out = new FileOutputStream ( "part_" + index)) { byte [] buffer = new byte [ 4096 ]; int bytesRead; while ((bytesRead = in.read(buffer)) != - 1 ) { out.write(buffer, 0 , bytesRead); } } } private static void mergeFiles ( int partCount) throws IOException { try ( FileOutputStream fos = new FileOutputStream ( "final_file" )) { for ( int i = 0 ; i < partCount; i++) { try ( FileInputStream fis = new FileInputStream ( "part_" + i)) { byte [] buffer = new byte [ 4096 ]; int bytesRead; while ((bytesRead = fis.read(buffer)) != - 1 ) { fos.write(buffer, 0 , bytesRead); } } } } } }
4.3 关键点分析
| 组件 | 作用 |
|---|---|
| CountDownLatch | 控制所有线程完成后再合并 |
| ExecutorService | 管理并发下载线程 |
| HttpURLConnection | 分段下载 |
| RandomAccessFile | 可选:支持写入指定位置,避免合并 |
4.4 优化建议
- 使用
RandomAccessFile直接写入文件偏移位置,避免合并; - 使用
CompletableFuture实现异步下载和合并; - 使用
FileChannel+MappedByteBuffer提升 IO 性能; - 增加断点续传逻辑(记录下载偏移量);
5. 实现请求上下文隔离
5.1 场景描述
在 Web 应用中,每个请求需要维护请求上下文信息 (如用户信息、日志上下文、事务信息等),使用 ThreadLocal 可以实现线程级别的数据隔离。
5.2 实现方式
public class RequestContext { private static final ThreadLocal<String> currentUser = new ThreadLocal <>(); public static void setCurrentUser (String user) { currentUser.set(user); } public static String getCurrentUser () { return currentUser.get(); } public static void clear () { currentUser.remove(); } } // 模拟请求处理 public class RequestHandler implements Runnable { private final String user; public RequestHandler (String user) { this .user = user; } @Override public void run () { try { RequestContext.setCurrentUser(user); System.out.println( "Processing request for: " + RequestContext.getCurrentUser()); // 模拟业务逻辑 Thread.sleep( 1000 ); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { RequestContext.clear(); } } public static void main (String[] args) { ExecutorService pool = Executors.newFixedThreadPool( 4 ); pool.submit( new RequestHandler ( "user1" )); pool.submit( new RequestHandler ( "user2" )); pool.shutdown(); } }
5.3 关键点分析
| 组件 | 作用 |
|---|---|
| ThreadLocal | 每个线程拥有独立副本,避免共享 |
| remove() | 避免内存泄漏(线程池中线程复用) |
| 线程池场景 | 确保 remove() 在任务结束时调用 |
5.4 优化建议
- 使用
InheritableThreadLocal支持父子线程传递; - 使用
TransmittableThreadLocal(阿里开源)解决线程池上下文传递问题; - 在
try-finally中调用remove(),防止内存泄漏; - 用于日志追踪(如 MDC)、用户上下文、事务管理等场景;
6. 支持并发的 LRU 缓存
-
使用
ConcurrentHashMap存储缓存; -
使用
ReentrantLock控制并发; -
支持自动淘汰最近最少使用的元素。
import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; public class LRUCache <K, V> { private final int maxSize; private final Map<K, V> cache = new ConcurrentHashMap <>(); private final Map<K, AtomicInteger> usageCount = new ConcurrentHashMap <>(); private final ReentrantLock lock = new ReentrantLock (); public LRUCache ( int maxSize) { this .maxSize = maxSize; } public V get (K key) { lock.lock(); try { usageCount.computeIfPresent(key, (k, v) -> new AtomicInteger (v.incrementAndGet())); return cache.get(key); } finally { lock.unlock(); } } public void put (K key, V value) { lock.lock(); try { if (cache.size() >= maxSize) { evict(); } cache.put(key, value); usageCount.put(key, new AtomicInteger ( 1 )); } finally { lock.unlock(); } } private void evict () { K lruKey = usageCount.entrySet().stream() .min(Map.Entry.comparingByValue((a, b) -> Integer.compare(a.get(), b.get()))) .map(Map.Entry::getKey) .orElse( null ); if (lruKey != null ) { cache.remove(lruKey); usageCount.remove(lruKey); } } }
7. 支持重试机制的异步下载器
-
使用
CompletableFuture实现异步下载; -
下载失败时自动重试;
-
支持设置最大重试次数。
import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; public class RetryableDownloader { private static final int MAX_RETRY = 3 ; public static void main (String[] args) { String url = "http://example.com/largefile.zip" ; String output = "downloaded_file" ; CompletableFuture<Void> future = CompletableFuture.runAsync(() -> { AtomicInteger retry = new AtomicInteger ( 0 ); while (retry.get() < MAX_RETRY) { try { downloadFile(url, output); System.out.println( "Download completed." ); return ; } catch (IOException e) { System.out.println( "Download failed, retrying..." ); retry.incrementAndGet(); } } System.out.println( "Download failed after " + MAX_RETRY + " retries." ); }); future.exceptionally(ex -> { System.out.println( "Error: " + ex.getMessage()); return null ; }); future.join(); } private static void downloadFile (String url, String output) throws IOException { // 模拟下载失败 if (Math.random() < 0.5 ) { throw new IOException ( "Simulated network error" ); } try ( InputStream in = new URL (url).openStream(); OutputStream out = new FileOutputStream (output)) { byte [] buffer = new byte [ 4096 ]; int bytesRead; while ((bytesRead = in.read(buffer)) != - 1 ) { out.write(buffer, 0 , bytesRead); } } } }
8. 线程安全的计数器
-
使用
LongAdder和ReentrantLock实现两个版本的线程安全计数器; -
对比性能差异。
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.LongAdder; import java.util.concurrent.locks.ReentrantLock; public class CounterComparison { private static final int THREAD_COUNT = 100 ; private static final int LOOP_COUNT = 100000 ; public static void main (String[] args) throws InterruptedException { // 使用 LongAdder LongAdder longAdder = new LongAdder (); ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT); long start = System.currentTimeMillis(); for ( int i = 0 ; i < THREAD_COUNT; i++) { executor.submit(() -> { for ( int j = 0 ; j < LOOP_COUNT; j++) { longAdder.increment(); } }); } executor.shutdown(); executor.awaitTermination( 1 , TimeUnit.MINUTES); System.out.println( "LongAdder result: " + longAdder.sum()); System.out.println( "LongAdder time: " + (System.currentTimeMillis() - start) + " ms" ); // 使用 ReentrantLock ReentrantLock lock = new ReentrantLock (); long counter = 0 ; ExecutorService executor2 = Executors.newFixedThreadPool(THREAD_COUNT); long start2 = System.currentTimeMillis(); for ( int i = 0 ; i < THREAD_COUNT; i++) { executor2.submit(() -> { for ( int j = 0 ; j < LOOP_COUNT; j++) { lock.lock(); try { counter++; } finally { lock.unlock(); } } }); } executor2.shutdown(); executor2.awaitTermination( 1 , TimeUnit.MINUTES); System.out.println( "ReentrantLock result: " + counter); System.out.println( "ReentrantLock time: " + (System.currentTimeMillis() - start2) + " ms" ); } }
9. 线程安全的配置中心
-
使用
CopyOnWriteArrayList存储配置; -
支持并发读取;
-
支持动态更新配置。
import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; public class ThreadSafeConfigCenter { private final List<String> configs = new CopyOnWriteArrayList <>(); public void addConfig (String config) { configs.add(config); } public void updateConfig ( int index, String newConfig) { configs.set(index, newConfig); } public void printAllConfigs () { for (String config : configs) { System.out.println(config); } } public static void main (String[] args) { ThreadSafeConfigCenter center = new ThreadSafeConfigCenter (); ExecutorService pool = Executors.newFixedThreadPool( 4 ); for ( int i = 0 ; i < 100 ; i++) { int finalI = i; pool.submit(() -> center.addConfig( "Config-" + finalI)); } for ( int i = 0 ; i < 10 ; i++) { int index = i % 100 ; int finalI = i; pool.submit(() -> center.updateConfig(index, "Updated-Config-" + finalI)); } pool.shutdown(); try { pool.awaitTermination( 1 , TimeUnit.MINUTES); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } center.printAllConfigs(); } }
10. 请求追踪系统
-
使用
ThreadLocal记录请求 ID; -
使用 MDC 记录日志上下文;
-
支持日志追踪。
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class RequestTracer { private static final ThreadLocal<String> requestId = new ThreadLocal <>(); private static final Logger logger = LoggerFactory.getLogger(RequestTracer.class); public static void main (String[] args) { ExecutorService pool = Executors.newFixedThreadPool( 4 ); for ( int i = 0 ; i < 10 ; i++) { String id = "req-" + i; pool.submit(() -> { requestId.set(id); MDC.put( "requestId" , id); logger.info( "Processing request..." ); requestId.remove(); MDC.clear(); }); } pool.shutdown(); } }
11. 线程安全的定时任务调度器
-
使用
ScheduledExecutorService实现定时任务; -
支持任务取消;
-
支持任务重试机制。
import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; public class SafeScheduledTask { public static void main (String[] args) { ScheduledExecutorService scheduler = Executors.newScheduledThreadPool( 2 ); ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(() -> { try { System.out.println( "Executing task..." ); } catch (Exception e) { System.err.println( "Task failed: " + e.getMessage()); } }, 0 , 1 , TimeUnit.SECONDS); // 5秒后取消任务 Executors.newSingleThreadScheduledExecutor().schedule(future::cancel, 5 , TimeUnit.SECONDS); } }
12. 总结
| 案例 | 核心知识点 | 适用场景 |
|---|---|---|
| 生产者-消费者 | BlockingQueue 、线程通信 | 任务队列、消息队列 |
| Future 异步处理 | ExecutorService 、 Future 、 Callable | 并行计算、异步任务 |
| 自定义阻塞队列 | ReentrantLock 、 Condition | 自定义任务调度、事件驱动 |
| 多线程下载 | HttpURLConnection 、 CountDownLatch | 大文件下载、IO 并发 |
| 请求上下文隔离 | ThreadLocal 、线程安全 | Web 请求处理、日志追踪 |
| LRU 缓存 | ConcurrentHashMap 、 ReentrantLock | 缓存管理、资源缓存 |
| 异步下载器 | CompletableFuture 、重试机制 | 并行下载、失败重试 |
| 线程安全计数器 | LongAdder 、 ReentrantLock | 高并发计数器 |
| 配置中心 | CopyOnWriteArrayList 、线程安全 | 读多写少的配置管理 |
| 请求追踪系统 | ThreadLocal 、MDC | 日志追踪、请求上下文隔离 |
| 定时任务调度器 | ScheduledExecutorService 、 Future | 定时采集、心跳检测 |