本文目录导读:

Java中使用悲观锁的案例
悲观锁的核心思想是:在访问共享资源前,先锁定资源,确保其他线程无法同时访问,以下是几种常见的实现方式:
synchronized 关键字(最基础)
public class PessimisticLockExample {
private int count = 0;
private final Object lock = new Object();
// 方法级别的悲观锁
public synchronized void increment() {
count++;
}
// 代码块级别的悲观锁(更灵活)
public void decrement() {
synchronized (lock) {
count--;
}
}
}
ReentrantLock(可重入锁)
import java.util.concurrent.locks.ReentrantLock;
public class BankAccount {
private double balance;
private final ReentrantLock lock = new ReentrantLock();
public BankAccount(double initialBalance) {
this.balance = initialBalance;
}
// 转账方法 - 使用悲观锁
public void transfer(double amount) {
lock.lock(); // 获取锁
try {
if (balance >= amount) {
System.out.println(Thread.currentThread().getName() + " 开始转账: " + amount);
Thread.sleep(100); // 模拟耗时操作
balance -= amount;
System.out.println(Thread.currentThread().getName() + " 转账成功,余额: " + balance);
} else {
System.out.println("余额不足,当前余额: " + balance);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock(); // 释放锁
}
}
public static void main(String[] args) {
BankAccount account = new BankAccount(1000);
// 模拟多个用户同时转账
for (int i = 0; i < 5; i++) {
new Thread(() -> {
account.transfer(200);
}).start();
}
}
}
数据库层面的悲观锁(Java + MySQL)
import java.sql.*;
public class DatabasePessimisticLock {
// 使用 SELECT ... FOR UPDATE 加悲观锁
public void updateWitnPessimisticLock(int productId, int quantity) {
String selectSql = "SELECT quantity FROM products WHERE id = ? FOR UPDATE";
String updateSql = "UPDATE products SET quantity = ? WHERE id = ?";
try (Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password");
PreparedStatement selectStmt = conn.prepareStatement(selectSql);
PreparedStatement updateStmt = conn.prepareStatement(updateSql)) {
// 开启事务
conn.setAutoCommit(false);
// 查询并加锁
selectStmt.setInt(1, productId);
ResultSet rs = selectStmt.executeQuery();
if (rs.next()) {
int currentQuantity = rs.getInt("quantity");
if (currentQuantity >= quantity) {
// 更新库存
updateStmt.setInt(1, currentQuantity - quantity);
updateStmt.setInt(2, productId);
updateStmt.executeUpdate();
conn.commit();
System.out.println("库存更新成功");
} else {
conn.rollback();
System.out.println("库存不足");
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
实际应用场景:库存扣减系统
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.atomic.AtomicInteger;
public class InventorySystem {
private AtomicInteger stock = new AtomicInteger(100);
private final ReentrantLock lock = new ReentrantLock(true); // 公平锁
// 使用悲观锁确保库存操作原子性
public boolean deductStock(int quantity) {
lock.lock();
try {
// 模拟业务处理时间
Thread.sleep(50);
int currentStock = stock.get();
if (currentStock >= quantity) {
// 扣减库存
stock.addAndGet(-quantity);
System.out.println(Thread.currentThread().getName() +
" 扣减成功,当前库存: " + stock.get());
return true;
} else {
System.out.println(Thread.currentThread().getName() +
" 库存不足,当前库存: " + currentStock + ",需要: " + quantity);
return false;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
InventorySystem system = new InventorySystem();
// 模拟并发购买
for (int i = 0; i < 10; i++) {
final int quantity = 10;
new Thread(() -> {
system.deductStock(quantity);
}, "用户-" + i).start();
}
}
}
性能对比与选择建议
| 特性 | synchronized | ReentrantLock | 数据库锁 |
|---|---|---|---|
| 使用简便性 | ✅ 简单 | ⚠️ 较复杂 | ❌ 复杂 |
| 性能 | ❌ 较差 | ✅ 较好 | ⚠️ 一般 |
| 灵活性 | ❌ 低 | ✅ 高 | ✅ 高 |
| 超时机制 | ❌ 不支持 | ✅ 支持 | ✅ 支持 |
| 公平性 | ❌ 非公平 | ✅ 可选择 | ⚠️ 取决于DB |
使用建议
- 小规模并发:使用
synchronized即可 - 高性能需求:使用
ReentrantLock - 分布式系统:使用数据库锁或分布式锁(如 Redis、Zookeeper)
- 锁的范围要小:尽量缩小加锁的代码块
- 避免死锁:注意锁的获取顺序,使用 tryLock 避免无限等待
在实际项目中,优先使用无锁或乐观锁(如 AtomicInteger、CAS),只有在冲突概率较高时才考虑悲观锁。