java并发编程中悲观锁和乐观锁是什么意思

发布时间:2021-12-24 09:57:26 作者:小新
来源:亿速云 阅读:114
# 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();  // 释放锁
    }
}

1.3 数据库中的悲观锁

-- MySQL的SELECT...FOR UPDATE语句
BEGIN;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
-- 更新操作
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;

二、乐观锁的基本概念

2.1 什么是乐观锁

乐观锁(Optimistic Locking)采用”先修改,再验证”的策略。其核心假设是:并发冲突不常发生,因此允许多个线程同时读取数据,但在更新时会检查数据是否被其他线程修改过。

2.2 典型实现方式

2.2.1 版本号机制

// 伪代码示例
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;
}

2.2.2 CAS操作

// AtomicInteger的CAS实现
AtomicInteger atomicInt = new AtomicInteger(0);

boolean success = atomicInt.compareAndSet(0, 1);  // 期望值0,新值1

2.3 数据库中的乐观锁

-- 使用版本号的SQL示例
UPDATE products 
SET stock = stock - 1, version = version + 1
WHERE id = 100 AND version = 2;

三、两种锁的对比分析

3.1 性能对比

指标 悲观锁 乐观锁
并发读 差(需要加锁) 优秀(无需加锁)
并发写 串行执行 可能失败但可重试
冲突频率 高冲突场景适用 低冲突场景适用
系统开销 上下文切换、锁维护成本高 仅CAS或版本检查,开销低

3.2 适用场景对比

悲观锁适用场景: 1. 写操作频繁且冲突概率高 2. 临界区代码执行时间长 3. 需要保证强一致性的场景(如银行转账)

乐观锁适用场景: 1. 读多写少的环境 2. 冲突概率低的场景 3. 需要高吞吐量的系统

四、Java中的具体实现

4.1 悲观锁实现细节

4.1.1 synchronized的底层原理

public synchronized void method() {
    // 方法体
}

对应的字节码:

monitorenter
// 方法代码
monitorexit

4.1.2 ReentrantLock的AQS实现

public class ReentrantLock implements Lock {
    private final Sync sync;
    abstract static class Sync extends AbstractQueuedSynchronizer {
        // 实现AQS的tryAcquire等方法
    }
}

4.2 乐观锁实现细节

4.2.1 Atomic类的实现

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);
    }
}

4.2.2 LongAdder的分段优化

public void add(long x) {
    Cell[] as; long b, v; int m; Cell a;
    if ((as = cells) != null || !casBase(b = base, b + x)) {
        // 使用分段Cell减少竞争
    }
}

五、实际应用案例

5.1 悲观锁案例:库存扣减

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;
        }
    }
}

5.2 乐观锁案例:用户积分更新

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;
    }
}

六、高级话题

6.1 锁升级与降级

// ReentrantReadWriteLock的锁降级示例
public void lockDowngrade() {
    writeLock.lock();
    try {
        // 写操作...
        readLock.lock();  // 锁降级开始
    } finally {
        writeLock.unlock();  // 降级完成
    }
    // 仍持有读锁...
    readLock.unlock();
}

6.2 ABA问题及解决方案

// 使用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);

七、总结与最佳实践

  1. 选择标准:根据冲突概率、性能要求和业务场景选择
  2. 混合使用:系统中可以同时存在两种锁机制
  3. 监控指标:关注锁竞争率、CAS成功率等关键指标
  4. 避免误区:不是所有场景都需要加锁,有时无锁数据结构更优

“并发控制没有银弹,理解每种策略的适用场景比掌握实现细节更重要。” —— Brian Goetz

参考资料

  1. 《Java并发编程实战》
  2. Oracle官方Java文档
  3. Java内存模型规范(JSR-133)

”`

注:本文实际约2300字,完整展开所有代码示例和详细解释后可达2500字左右。可根据需要调整具体章节的深度。

推荐阅读:
  1. MySQL中乐观锁和悲观锁是什么
  2. MySQL中乐观锁和悲观锁的区别

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java

上一篇:3.19 Paydex携手vtoken示例分析

下一篇:linux中如何删除用户组

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》