Java并发编程中的锁分类
1. 按锁的设计思想划分
1.1 乐观锁(Optimistic Locking)
- 原理:假设并发冲突概率低,先操作数据,提交时检测是否冲突(如版本号、时间戳或CAS)。
- 实现:
CAS
(Compare-And-Swap)、AtomicInteger
等原子类。 - 场景:读多写少,冲突概率低(如计数器、状态标记)。
- 缺点:频繁冲突时性能下降。
1.2 悲观锁(Pessimistic Locking)
- 原理:假设并发冲突概率高,操作前先加锁(独占资源)。
- 实现:
synchronized
、ReentrantLock
。 - 场景:写多读少,临界区复杂(如银行转账)。
- 缺点:线程阻塞导致上下文切换开销。
2. 按锁的可重入性划分
2.1 可重入锁(Reentrant Lock)
- 特点:同一线程可重复获取同一锁,避免死锁。
- 实现:
synchronized
、ReentrantLock
。 - 场景:递归调用或需要多层加锁的代码。
2.2 不可重入锁
- 特点:线程重复获取同一锁会阻塞自身。
- 实现:需手动实现(Java标准库无直接支持)。
- 场景:特殊需求,如限制递归调用。
3. 按锁的公平性划分
3.1 公平锁(Fair Lock)
- 特点:按请求顺序分配锁,避免饥饿。
- 实现:
ReentrantLock(true)
。 - 缺点:吞吐量较低(需维护队列)。
3.2 非公平锁(Non-Fair Lock)
- 特点:允许插队,可能提高吞吐量。
- 实现:
synchronized
、ReentrantLock()
(默认)。 - 场景:高并发且锁持有时间短。
4. 按锁的共享性划分
4.1 排他锁(Exclusive Lock)
- 特点:独占资源,其他线程无法读写。
- 实现:
synchronized
、ReentrantLock
、ReentrantReadWriteLock.WriteLock
。 - 场景:写操作需互斥。
4.2 共享锁(Shared Lock)
- 特点:允许多线程并发读,但排斥写。
- 实现:
ReentrantReadWriteLock.ReadLock
、Semaphore
。 - 场景:读多写少(如缓存)。
5. 按锁的实现机制划分
5.1 内置锁(Intrinsic Lock)
- 特点:JVM实现,自动管理锁获取与释放。
- 实现:
synchronized
关键字。 - 场景:简单同步需求。
5.2 显式锁(Explicit Lock)
- 特点:需手动管理,支持更多功能(如超时、中断)。
- 实现:
ReentrantLock
、StampedLock
。 - 场景:复杂同步需求(如条件变量)。
6. 按线程等待策略划分
6.1 自旋锁(Spin Lock)
- 特点:循环尝试获取锁,避免上下文切换。
- 实现:
AtomicInteger
的CAS操作。 - 场景:锁持有时间极短(如短循环内的原子操作)。
6.2 阻塞锁(Blocking Lock)
- 特点:获取失败时线程进入阻塞状态。
- 实现:
synchronized
、ReentrantLock.lock()
。 - 场景:锁持有时间较长。
7. 按锁的优化状态划分(针对synchronized)
7.1 偏向锁(Biased Locking)
- 特点:假设单线程访问,直接进入临界区。
- 场景:无竞争环境(如单例初始化)。
7.2 轻量级锁(Lightweight Locking)
- 特点:通过CAS竞争锁,失败则升级为重量级锁。
- 场景:低竞争环境。
7.3 重量级锁(Heavyweight Locking)
- 特点:依赖操作系统互斥量(Mutex),线程阻塞。
- 场景:高竞争环境。
8. 按锁的应用策略划分
8.1 分段锁(Segment Lock)
- 原理:将数据分块,每块独立加锁(降低竞争)。
- 实现:
ConcurrentHashMap
(JDK 1.7及之前)。 - 场景:大数据量且并发更新频繁。
8.2 条件锁(Condition Lock)
- 特点:基于条件变量协调线程(如等待/唤醒)。
- 实现:
Condition
接口(配合ReentrantLock
使用)。 - 场景:生产者-消费者模型。
总结
- 选型建议:根据场景选择锁类型。例如,读多写少用
ReadWriteLock
,简单同步用synchronized
,复杂需求用ReentrantLock
。 - 注意事项:避免死锁(按顺序加锁)、活锁(随机退避)及锁粒度问题(过粗降低并发,过细增加开销)。
通过合理选择锁策略,可以在保证线程安全的同时最大化并发性能。