Java乐观锁和悲观锁的区别
引言
在多线程并发编程中,为了保证数据的一致性和避免出现数据竞争的问题,我们需要使用锁机制。而乐观锁和悲观锁是常用的两种锁机制。本文将介绍Java中乐观锁和悲观锁的区别以及如何实现它们。
乐观锁和悲观锁的概念
乐观锁和悲观锁是并发编程中的两个重要概念。乐观锁的思想是假设在数据的修改和提交过程中不会发生冲突,因此不加锁,而是在更新数据时进行一次判断。如果发现冲突,则进行回滚或重试。悲观锁则是假设在数据的修改和提交过程中会发生冲突,因此在访问数据之前就加上锁,保证数据操作的原子性。
乐观锁和悲观锁的区别
| 乐观锁 | 悲观锁 | |
|---|---|---|
| 思想 | 假设没有冲突,不加锁 | 假设会有冲突,加锁 | 
| 适用场景 | 冲突较少,读操作多 | 冲突较多,写操作多 | 
| 实现方式 | 使用版本号或时间戳进行判断 | 使用synchronized、Lock等锁机制 | 
| 阻塞 | 不会阻塞其他线程 | 会阻塞其他线程 | 
| 性能 | 性能较好,适用于读操作多的场景 | 性能较差,适用于写操作多的场景 | 
实现乐观锁和悲观锁的步骤
1. 乐观锁的实现步骤
public class OptimisticLockingExample {
    private int value;
    private int version;
    public void updateValue(int newValue) {
        // 读取当前版本号
        int currentVersion = version;
        // 模拟耗时操作
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 判断版本号是否发生变化
        if (currentVersion == version) {
            value = newValue;
            version++;
            System.out.println("更新成功");
        } else {
            System.out.println("更新失败,数据已被修改");
        }
    }
}
2. 悲观锁的实现步骤
public class PessimisticLockingExample {
    private int value;
    private Object lock = new Object();
    public void updateValue(int newValue) {
        // 加锁
        synchronized (lock) {
            // 模拟耗时操作
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            value = newValue;
            System.out.println("更新成功");
        }
    }
}
流程图
st=>start: 开始
op1=>operation: 读取当前版本号
op2=>operation: 模拟耗时操作
op3=>operation: 判断版本号是否发生变化
cond=>condition: 是否发生变化?
op4=>operation: 更新数据和版本号
e=>end: 结束
st->op1->op2->op3->cond
cond(yes)->op4->e
cond(no)->e
数学公式
乐观锁的判断条件:currentVersion == version
总结
乐观锁和悲观锁是并发编程中常用的两种锁机制。乐观锁假设数据操作不会发生冲突,通过版本号或时间戳进行判断和重试;悲观锁假设数据操作会发生冲突,通过加锁确保数据的原子性。在选择锁机制时需要根据具体的业务场景进行权衡,以提高程序的并发性和性能。










