文章目录
前言:锁的七十二变
多线程编程就像开碰碰车(线程)进游乐场(共享资源),没点规则肯定要撞车!!今天咱们来扒一扒Java中那些让人又爱又恨的锁策略,看完保证你写线程安全代码时不再靠玄学(疯狂踩坑)!
一、乐观锁 vs 悲观锁(心态大不同)
1.1 悲观锁:总有刁民想害朕
// 典型代表:synchronized关键字
public synchronized void transfer(Account target, int amount) {
// 默认认为每次操作都会冲突,直接上锁!
this.balance -= amount;
target.balance += amount;
}
使用场景:写操作频繁、冲突概率高的场景(比如秒杀系统)
1.2 乐观锁:世界充满爱
// 典型实现:CAS(Compare And Swap)
AtomicInteger count = new AtomicInteger(0);
public void safeIncrement() {
int oldValue;
do {
oldValue = count.get(); // 先读取当前值
} while (!count.compareAndSet(oldValue, oldValue + 1)); // 尝试更新
}
(敲黑板):版本号机制在数据库中的应用:
UPDATE products
SET stock = stock - 1, version = version + 1
WHERE id = 100 AND version = 1; -- 这里的version就是乐观锁
二、读写锁(读多写少的神器)
2.1 读写分离的智慧
ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
void readData() {
rwLock.readLock().lock();
try {
// 多个线程可以同时读
} finally {
rwLock.readLock().unlock();
}
}
void writeData() {
rwLock.writeLock().lock();
try {
// 同一时间只允许一个线程写
} finally {
rwLock.writeLock().unlock();
}
}
性能对比:在100次读+1次写的场景下,读写锁比互斥锁快10倍以上!!!
三、自旋锁(头铁的锁)
3.1 旋转跳跃我不停歇
// AtomicInteger的自旋实现
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}
适用场景:
- 线程阻塞代价高(内核态切换)
- 临界区代码执行时间极短(建议小于1ms)
(注意):长时间自旋会疯狂消耗CPU,慎用!!
四、公平锁 vs 非公平锁(排队哲学)
4.1 公平锁的强迫症
new ReentrantLock(true); // 开启公平模式
优点:不会饿死线程
代价:吞吐量下降约30%(要维护队列)
4.2 非公平锁的狼性文化
new ReentrantLock(); // 默认非公平
实测场景:10个线程竞争时,非公平锁的吞吐量是公平锁的2倍!
五、可重入锁(套娃高手)
5.1 递归不翻车的秘密
public synchronized void methodA() {
methodB(); // 可以重复获取同一把锁
}
public synchronized void methodB() {
// ...
}
(灵魂拷问):如果synchronized不可重入,递归调用会怎样?→ 直接死锁!!
锁策略选择速查表
| 场景特征 | 推荐锁策略 | 避坑指南 |
|---|---|---|
| 读多写少(如缓存) | 读写锁 | 注意写锁的获取顺序 |
| 临界区执行时间极短(<1ms) | 自旋锁 | 避免长时间自旋 |
| 需要保证绝对公平 | 公平锁 | 做好性能下降的心理准备 |
| 线程切换代价高(内核态操作) | 自旋锁+超时机制 | 设置合理的自旋次数 |
| 嵌套调用/递归场景 | 可重入锁 | 注意锁的释放次数 |
| 写操作频繁(如库存扣减) | 悲观锁 | 配合事务使用 |
实战中的骚操作
锁升级策略(JDK6+)
- 无锁 → 偏向锁 → 轻量级锁 → 重量级锁
- 对象头中的Mark Word会记录锁状态变化
锁消除(JVM优化)
// 虽然用了synchronized,但JVM检测到没有线程安全问题
public String concat(String s1, String s2) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
return sb.toString();
}
(神奇):JVM会自动去掉这里的同步锁!
结语:没有银弹
实际开发中经常需要混合使用多种锁策略,比如:
- 读写锁 + 乐观锁(如MySQL的MVCC)
- 自旋锁 + 超时机制(防止死等)
- 可重入锁 + 公平性选择
记住:锁的粒度越细,性能越好,但复杂度越高。大家在面试被问锁的时候,可以甩出今天这些干货,绝对让面试官眼前一亮(亲测有效)!!
10万+

被折叠的 条评论
为什么被折叠?



