⚙️ 一、并发核心概念与挑战
- 并发 vs 并行
- 并发:单核上任务交替执行(如Web服务器处理多请求),通过时间片切换模拟“同时”效果。
- 并行:多核上任务真正同时执行(如大数据并行计算),依赖硬件支持。
- 误区:混淆两者可能导致技术选型错误(例如Python多线程处理CPU密集型任务因GIL失效)。
- 三大核心问题
- 原子性:操作不可中断(如
i++
需加锁或原子类)。 - 可见性:线程修改共享变量后其他线程立即可见(
volatile
或内存屏障解决)。 - 有序性:禁止指令重排序(Happens-Before原则保障)。
- 原子性:操作不可中断(如
- 典型风险问题类型表现解决方案死锁线程相互等待资源释放避免嵌套锁,使用
tryLock
超时竞态条件执行结果依赖线程执行顺序同步块或原子操作线程饥饿低优先级线程长期未获资源公平锁或合理调度策略
🧩 二、线程同步工具实战技巧
- Semaphore(信号量)
- 场景:资源池限流(如数据库连接池)。
- 示例:控制秒杀系统并发订单处理数:
// 根据CPU核心数动态设置许可数 int permits = Runtime.getRuntime().availableProcessors() * 2; Semaphore orderSemaphore = new Semaphore(permits); orderSemaphore.acquire(); // 获取许可 try { processOrder(); } finally { orderSemaphore.release(); } // 必须释放!
- 高级用法:
tryAcquire(500, TimeUnit.MILLISECONDS)
实现超时降级。
- CountDownLatch(倒计时器)
- 场景:多任务完成后触发主任务(如初始化系统需加载所有配置)。
- 示例:线程池优雅关闭:
CountDownLatch latch = new CountDownLatch(taskCount); executor.submit(() -> { try { runTask(); } finally { latch.countDown(); } // 任务完成计数减1 }); latch.await(); // 阻塞至所有任务完成 executor.shutdown();
- 避坑:初始计数必须与
countDown()
调用次数一致,否则永久阻塞。
- CyclicBarrier & Phaser
- 场景:分阶段任务协同(如游戏多玩家回合制操作)。
- 对比:工具重用性适用场景
CountDownLatch
一次性单次多任务等待CyclicBarrier
可重置多轮次协同Phaser
动态调整复杂分层任务(如MapReduce)
🚀 三、并发模型选型指南
根据任务类型选择合适模型:
- CPU密集型(如图像处理):→ 多进程:绕过GIL(Python)或利用多核(Java/C++)。→ 工具:
ProcessPoolExecutor
(Python)、ForkJoinPool
(Java)。 - I/O密集型(如网络请求):→ 异步IO:
asyncio
(Python)、CompletableFuture
(Java)。→ 线程池:ThreadPoolExecutor
(最大线程数按I/O延迟调整)。 - 混合型任务:→ Hybrid模式:异步协程 + 线程池处理阻塞调用。
性能对比案例(Python HTTP请求):
- 同步版:10次请求耗时10.8秒
- 异步版(
asyncio + aiohttp
):仅1.2秒,效率提升9倍
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
暂无评论内容