悠悠楠杉
Java中锁的分类及机制详解
一、锁的本质与分类体系
在多线程编程中,锁是协调资源访问的核心机制。Java的锁体系可分为三个维度:
- 按线程竞争策略:悲观锁 vs 乐观锁
- 按锁的公平性:公平锁 vs 非公平锁
- 按实现层级:JVM内置锁 vs JDK显式锁
二、悲观锁与乐观锁
1. 悲观锁(Pessimistic Locking)
认为并发冲突必然发生,典型代表是synchronized
关键字:
java
public synchronized void transfer(Account target, int amount) {
this.balance -= amount;
target.balance += amount;
}
特性:
- 独占资源直至释放
- 适合写操作频繁场景
- 可能引发线程阻塞
2. 乐观锁(Optimistic Locking)
假设冲突概率低,采用版本号/CAS机制实现。如AtomicInteger
:
java
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet(); // 基于CAS操作
实现要点:
- 比较并交换(Compare-And-Swap)
- 适合读多写少场景
- 需处理失败重试
三、公平锁与非公平锁
1. 公平锁(Fair Lock)
按照线程申请顺序分配资源,如:
java
ReentrantLock fairLock = new ReentrantLock(true);
特点:
- 避免线程饥饿
- 上下文切换开销较大
2. 非公平锁(Nonfair Lock)
允许插队抢占资源,默认实现:
java
ReentrantLock nonFairLock = new ReentrantLock();
优势:
- 吞吐量更高
- 可能出现长时等待
四、重量级锁与轻量级锁
1. 重量级锁
传统synchronized
在竞争激烈时会升级为重量级锁,涉及操作系统互斥量。
2. 轻量级锁
通过CAS操作尝试获取锁,失败才升级为重量级锁。JVM的锁优化包括:
- 偏向锁
- 自旋锁
- 锁消除
五、可重入锁(ReentrantLock)
允许同一线程多次获取同一把锁:
```java
ReentrantLock lock = new ReentrantLock();
void recursiveMethod(int n) {
lock.lock();
try {
if(n > 0) {
recursiveMethod(n-1);
}
} finally {
lock.unlock();
}
}
```
优势:
- 避免自我死锁
- 支持条件变量(Condition)
六、死锁产生与预防
典型死锁场景:
```java
// 线程1
synchronized(resourceA) {
synchronized(resourceB) {...}
}
// 线程2
synchronized(resourceB) {
synchronized(resourceA) {...}
}
```
预防策略:
1. 按固定顺序获取资源
2. 设置超时时间(tryLock)
3. 使用死锁检测工具
七、锁的性能优化建议
- 缩小同步代码块范围
- 读写分离时采用
ReadWriteLock
- 考虑无锁数据结构(如ConcurrentHashMap)
- 监控锁竞争情况(JVisualVM)
通过合理选择锁机制,开发者可以在线程安全与系统性能之间取得平衡。理解不同锁的特性及适用场景,是构建高并发系统的关键基础。
```