1. 并发编程的考古与演进
最近在整理技术笔记时,翻出了十年前写的并发编程代码,不禁感慨技术演进的快速。那些曾经引以为傲的同步锁设计,如今看来就像出土文物般充满时代感。这促使我系统梳理了并发编程的发展脉络,也发现了一些值得分享的思考。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 并发模型的演进历程
2.1 石器时代:原始同步机制
早期Java开发者最熟悉的莫过于synchronized关键字。记得2010年做支付系统时,我们会在关键方法上加这个修饰符,简单粗暴但有效:
java复制public synchronized void transfer(Account from, Account to, BigDecimal amount) {
// 转账逻辑
}
这种方式的典型问题是:
- 锁粒度太粗导致性能瓶颈
- 容易产生死锁(特别是跨方法调用时)
- 无法设置超时时间
2.2 青铜时代:显式锁的出现
Java 5引入的java.util.concurrent包带来了更灵活的Lock接口。我们项目在2013年重构时全面改用ReentrantLock:
java复制private final Lock lock = new ReentrantLock();
public void transfer(Account from, Account to, BigDecimal amount) {
lock.lock();
try {
// 转账逻辑
} finally {
lock.unlock();
}
}
改进点包括:
- 可中断的锁获取
- 超时尝试
- 公平性设置
- 条件变量支持
2.3 铁器时代:原子变量与CAS
随着硬件发展,基于CAS(Compare-And-Swap)的原子类开始流行。我们在2015年做高频交易系统时,AtomicLong成为计数器的最佳选择:
java复制private AtomicLong counter = new AtomicLong(0);
public void increment() {
counter.incrementAndGet();
}
优势显而易见:
- 无锁设计提升吞吐量
- 内存可见性保证
- 轻量级操作
3. 现代并发编程实践
3.1 并发容器选择
当前项目中最常用的并发容器是ConcurrentHashMap。但在使用时有几个关键点需要注意:
- size()方法返回的是近似值
- 批量操作(如putAll)不是原子的
- 计算型方法(computeIfAbsent)要注意避免死锁
3.2 CompletableFuture的应用
异步编程方面,CompletableFuture已经成为主流。最近在实现订单超时取消功能时,我是这样使用的:
java复制CompletableFuture.supplyAsync(() -> orderService.createOrder(request))
.thenApplyAsync(order -> paymentService.processPayment(order))
.thenAcceptAsync(order -> inventoryService.updateStock(order))
.exceptionally(ex -> {
// 异常处理
return null;
});
3.3 虚拟线程的崛起
Java 19引入的虚拟线程(Virtual Thread)可能是近年来最重要的并发创新。与传统线程池相比:
| 特性 | 平台线程 | 虚拟线程 |
|---|---|---|
| 内存占用 | ~1MB | ~1KB |
| 创建成本 | 高 | 极低 |
| 上下文切换成本 | 高(涉及OS调度) | 低(JVM管理) |
| 适用场景 | CPU密集型任务 | IO密集型任务 |
4. 并发编程的陷阱与经验
4.1 死锁预防策略
在金融系统中我们遵循这些原则:
- 按固定顺序获取多把锁
- 使用tryLock设置超时
- 避免在持有锁时调用外部方法
4.2 性能优化技巧
通过JMH基准测试发现:
- 读多写少场景:StampedLock比ReentrantReadWriteLock快30%
- 计数器场景:LongAdder比AtomicLong吞吐量高5倍
- 短时锁竞争:偏向锁优化仍然有效
4.3 调试工具推荐
- jstack:分析线程转储
- Arthas:实时监控锁竞争
- VisualVM:查看线程状态
- JProfiler:定位锁热点
5. 其他技术方向的思考
5.1 响应式编程的局限
虽然Reactive宣言很美好,但在实际企业应用中我们发现:
- 调试困难(调用栈断裂)
- 与传统库兼容性差
- 学习曲线陡峭
- 不适合所有场景
5.2 云原生时代的并发
Kubernetes环境带来新挑战:
- Pod重启时的线程安全
- 分布式锁的实现
- 弹性伸缩时的资源协调
5.3 硬件层面的影响
现代CPU架构对并发编程的影响:
- 伪共享问题(False Sharing)
- NUMA架构的内存访问
- 持久化内存带来的变革
6. 未来展望
虽然协程、Actor模型等新技术不断涌现,但理解基础原理永远重要。建议每个开发者:
- 阅读Doug Lea的并发编程著作
- 定期review自己三年前写的并发代码
- 保持对硬件发展的关注
- 在合适场景尝试新特性
最后分享一个真实案例:去年我们通过将synchronized改为StampedLock,使订单处理吞吐量从1200 TPS提升到8500 TPS。这再次证明,并发优化是永无止境的旅程。
