您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# Java并发编程中悲观锁和乐观锁是什么意思
## 引言
在多线程并发编程中,保证数据的一致性和线程安全是核心挑战。Java提供了多种锁机制来解决并发问题,其中悲观锁和乐观锁是两种最基础且重要的并发控制策略。本文将深入探讨这两种锁的概念、实现原理、典型应用场景以及它们的优缺点对比。
## 一、悲观锁的基本概念
### 1.1 什么是悲观锁
悲观锁(Pessimistic Locking)是一种"先获取锁,再访问数据"的并发控制策略。其核心思想是:**假定并发冲突一定会发生**,因此在数据被访问前就加锁,确保同一时刻只有一个线程能操作数据。
### 1.2 典型实现方式
在Java中,悲观锁主要通过以下方式实现:
```java
// synchronized关键字实现
public synchronized void pessimisticMethod() {
// 临界区代码
}
// ReentrantLock实现
private Lock lock = new ReentrantLock();
public void lockExample() {
lock.lock(); // 获取锁
try {
// 临界区代码
} finally {
lock.unlock(); // 释放锁
}
}
-- MySQL的SELECT...FOR UPDATE语句
BEGIN;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
-- 更新操作
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;
乐观锁(Optimistic Locking)采用”先修改,再验证”的策略。其核心假设是:并发冲突不常发生,因此允许多个线程同时读取数据,但在更新时会检查数据是否被其他线程修改过。
// 伪代码示例
public boolean updateWithOptimisticLock(Entity entity) {
// 获取当前版本号
int currentVersion = entity.getVersion();
// 执行更新操作,同时检查版本号
int updatedRows = executeUpdate(
"UPDATE table SET field = ?, version = version + 1 " +
"WHERE id = ? AND version = ?",
newValue, entity.getId(), currentVersion);
return updatedRows > 0;
}
// AtomicInteger的CAS实现
AtomicInteger atomicInt = new AtomicInteger(0);
boolean success = atomicInt.compareAndSet(0, 1); // 期望值0,新值1
-- 使用版本号的SQL示例
UPDATE products
SET stock = stock - 1, version = version + 1
WHERE id = 100 AND version = 2;
指标 | 悲观锁 | 乐观锁 |
---|---|---|
并发读 | 差(需要加锁) | 优秀(无需加锁) |
并发写 | 串行执行 | 可能失败但可重试 |
冲突频率 | 高冲突场景适用 | 低冲突场景适用 |
系统开销 | 上下文切换、锁维护成本高 | 仅CAS或版本检查,开销低 |
悲观锁适用场景: 1. 写操作频繁且冲突概率高 2. 临界区代码执行时间长 3. 需要保证强一致性的场景(如银行转账)
乐观锁适用场景: 1. 读多写少的环境 2. 冲突概率低的场景 3. 需要高吞吐量的系统
public synchronized void method() {
// 方法体
}
对应的字节码:
monitorenter
// 方法代码
monitorexit
public class ReentrantLock implements Lock {
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
// 实现AQS的tryAcquire等方法
}
}
public class AtomicInteger extends Number {
private volatile int value;
private static final Unsafe unsafe = Unsafe.getUnsafe();
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
}
public void add(long x) {
Cell[] as; long b, v; int m; Cell a;
if ((as = cells) != null || !casBase(b = base, b + x)) {
// 使用分段Cell减少竞争
}
}
public class InventoryService {
private int stock = 100;
private final Object lock = new Object();
public boolean deductStock(int quantity) {
synchronized (lock) {
if (stock >= quantity) {
stock -= quantity;
return true;
}
return false;
}
}
}
public class UserPointsService {
public boolean addPoints(Long userId, int pointsToAdd) {
User user = userDao.get(userId);
int currentVersion = user.getVersion();
user.setPoints(user.getPoints() + pointsToAdd);
return userDao.updateWithVersion(user, currentVersion) > 0;
}
}
// ReentrantReadWriteLock的锁降级示例
public void lockDowngrade() {
writeLock.lock();
try {
// 写操作...
readLock.lock(); // 锁降级开始
} finally {
writeLock.unlock(); // 降级完成
}
// 仍持有读锁...
readLock.unlock();
}
// 使用AtomicStampedReference解决ABA问题
AtomicStampedReference<Integer> atomicRef =
new AtomicStampedReference<>(100, 0);
int[] stampHolder = new int[1];
int currentStamp = atomicRef.getStamp();
atomicRef.compareAndSet(100, 101, currentStamp, currentStamp + 1);
“并发控制没有银弹,理解每种策略的适用场景比掌握实现细节更重要。” —— Brian Goetz
”`
注:本文实际约2300字,完整展开所有代码示例和详细解释后可达2500字左右。可根据需要调整具体章节的深度。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。