您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# Java中如何实现悲观锁与乐观锁
## 前言
在多线程并发编程中,锁机制是保证数据一致性的重要手段。Java提供了多种锁实现方式,主要分为悲观锁和乐观锁两大类。本文将深入探讨这两种锁的实现原理、适用场景及具体代码实现,帮助开发者根据业务需求选择合适的并发控制策略。
---
## 一、锁机制基础概念
### 1.1 什么是悲观锁
悲观锁(Pessimistic Locking)假定并发冲突一定会发生,因此在数据被访问前先加锁,确保同一时刻只有一个线程能操作数据。典型实现包括:
- `synchronized`关键字
- `ReentrantLock`等显式锁
- 数据库中的`SELECT...FOR UPDATE`
### 1.2 什么是乐观锁
乐观锁(Optimistic Locking)假定并发冲突较少发生,只在数据提交时检查是否被修改过。典型实现方式:
- 版本号机制(Version)
- CAS(Compare-And-Swap)操作
- 数据库中的乐观锁实现
---
## 二、悲观锁的实现方式
### 2.1 synchronized关键字
```java
public class SynchronizedExample {
private int count = 0;
// 同步方法
public synchronized void increment() {
count++;
}
// 同步代码块
public void incrementWithBlock() {
synchronized(this) {
count++;
}
}
}
特点: - JVM内置实现,无需手动释放 - 可重入(同一线程可重复获取) - 非公平锁
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock(); // 获取锁
try {
count++;
} finally {
lock.unlock(); // 必须手动释放
}
}
}
优势:
- 可中断锁
- 尝试获取锁(tryLock()
)
- 可实现公平锁
- 支持多个条件变量(Condition)
// MySQL示例
Connection conn = DriverManager.getConnection(url, user, password);
conn.setAutoCommit(false);
try {
// 使用FOR UPDATE加锁
PreparedStatement stmt = conn.prepareStatement(
"SELECT * FROM accounts WHERE id = ? FOR UPDATE");
stmt.setInt(1, accountId);
ResultSet rs = stmt.executeQuery();
// 处理数据...
conn.commit();
} catch (SQLException e) {
conn.rollback();
}
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicExample {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet(); // 底层基于CAS
}
public void customUpdate() {
int oldValue, newValue;
do {
oldValue = count.get();
newValue = oldValue + 2; // 自定义计算
} while (!count.compareAndSet(oldValue, newValue));
}
}
支持的原子类:
- AtomicInteger
/AtomicLong
- AtomicReference
- AtomicStampedReference
(解决ABA问题)
数据库实现示例:
UPDATE products
SET stock = stock - 1,
version = version + 1
WHERE id = ? AND version = ?
Java代码配合实现:
public boolean updateWithVersion(Product product) {
int affectedRows = jdbcTemplate.update(
"UPDATE products SET stock = ?, version = version + 1 " +
"WHERE id = ? AND version = ?",
product.getStock(),
product.getId(),
product.getVersion());
return affectedRows > 0;
}
import java.util.concurrent.atomic.LongAdder;
public class LongAdderExample {
private LongAdder counter = new LongAdder();
public void increment() {
counter.increment();
}
public long getCount() {
return counter.sum();
}
}
适用场景: 高并发写多读少的计数器场景
特性 | 悲观锁 | 乐观锁 |
---|---|---|
并发度 | 低 | 高 |
开销 | 大(上下文切换) | 小(无阻塞) |
冲突频率 | 适合高冲突场景 | 适合低冲突场景 |
实现复杂度 | 简单 | 需处理重试逻辑 |
是否需要保证强一致性?
├── 是 → 悲观锁
└── 否 → 冲突概率如何?
├── 高 → 悲观锁
└── 低 → 乐观锁
public class HybridLockExample {
private final ReentrantLock lock = new ReentrantLock();
private AtomicInteger atomicCount = new AtomicInteger(0);
// 粗粒度悲观锁+细粒度乐观锁
public void hybridOperation() {
lock.lock();
try {
// 需要强一致性的操作
atomicCount.compareAndSet(getExpected(), getNewValue());
} finally {
lock.unlock();
}
}
}
// 使用tryLock避免死锁
public boolean transferWithTimeout(Account from, Account to,
long amount, long timeout)
throws InterruptedException {
long stopTime = System.nanoTime() + timeout;
while (true) {
if (from.lock.tryLock()) {
try {
if (to.lock.tryLock()) {
try {
// 执行转账操作
return true;
} finally {
to.lock.unlock();
}
}
} finally {
from.lock.unlock();
}
}
if (System.nanoTime() > stopTime) return false;
Thread.sleep(random.nextInt(100));
}
}
AtomicStampedReference<Integer> atomicRef =
new AtomicStampedReference<>(100, 0);
// 更新时检查版本戳
int[] stampHolder = new int[1];
int current = atomicRef.get(stampHolder);
atomicRef.compareAndSet(current, 200, stampHolder[0], stampHolder[0]+1);
public class VarHandleExample {
private int count = 0;
private static final VarHandle COUNT_HANDLE;
static {
try {
COUNT_HANDLE = MethodHandles.lookup()
.findVarHandle(VarHandleExample.class, "count", int.class);
} catch (Exception e) {
throw new Error(e);
}
}
public void increment() {
int oldValue;
do {
oldValue = (int) COUNT_HANDLE.get(this);
} while (!COUNT_HANDLE.compareAndSet(this, oldValue, oldValue+1));
}
}
// 悲观锁在虚拟线程中的使用
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 10_000; i++) {
executor.submit(() -> {
synchronized(lockObject) { // 不会阻塞OS线程
// 临界区操作
}
});
}
}
选择合适的锁机制需要综合考虑业务场景、冲突概率和性能要求。建议: 1. 优先考虑无锁设计 2. 低冲突场景用乐观锁 3. 高冲突或强一致性需求用悲观锁 4. 监控系统性能指标持续优化
通过合理运用这些并发控制技术,可以构建出高性能且线程安全的Java应用系统。 “`
注:本文实际约4500字,完整5050字版本需要扩展以下内容: 1. 更多性能测试数据对比 2. 分布式锁的实现方案 3. 具体框架(如Spring)中的集成示例 4. JVM层实现原理分析 5. 完整的生产级案例代码
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。