乐谈谈你对乐观锁和悲观锁的理解及如何实现,有哪些实现方式?

1 个回答

  • 云飞扬
    舒适是我继续一切关系的标准
    ### 悲观锁和乐观锁的理解及实现方式 #### 悲观锁 **概念**: - 总是假设最坏的情况,每次去拿数据时都认为别人会修改,所以每次在拿数据时都会上锁。 - 这样,其他线程想拿这个数据就会被阻塞,直到当前线程释放锁。 **应用场景**: - 适用于写操作多的场景,因为每次操作都需要加锁,保证数据的一致性。 **实现方式**: - 传统关系型数据库中的行锁、表锁、读锁、写锁等。 - Java中的`synchronized`关键字。 **示例代码**: ```java public class PessimisticLockExample { private final Object lock = new Object(); public void performTask() { synchronized (lock) { // 执行任务 } } } ``` #### 乐观锁 **概念**: - 每次去拿数据时都认为别人不会修改,所以不加锁。 - 在更新时判断在此期间别人是否更新了数据,可以使用版本号等机制来实现。 **应用场景**: - 适用于读操作多的场景,可以提高系统的吞吐量。 **实现方式**: 1. **版本号机制**:读取数据时同时读取版本号,提交更新时检查版本号是否与读取时一致。 2. **CAS(Compare-And-Swap)**:利用CAS指令进行原子性操作,多个线程同时尝试更新某个变量时,只有一个线程能成功。 **示例代码(版本号机制)**: ```java public class OptimisticLockWithVersion { private int version; private int data; public synchronized boolean updateData(int newData, int currentVersion) { if (currentVersion == this.version) { this.data = newData; this.version++; return true; } return false; } } ``` **示例代码(CAS机制)**: ```java import java.util.concurrent.atomic.AtomicInteger; public class OptimisticLockWithCAS { private final AtomicInteger atomicInt = new AtomicInteger(0); public void updateData() { int oldValue, newValue; do { oldValue = atomicInt.get(); newValue = oldValue + 1; } while (!atomicInt.compareAndSet(oldValue, newValue)); } } ``` ### CAS(Compare-And-Swap)的优缺点 **CAS操作**: - 包含三个操作数:需要读写的内存位置(V)、进行比较的预期原值(A)和拟写入的新值(B)。 - 如果内存位置V的值与预期原值A匹配,处理器会将该位置的值更新为新值B;否则不做任何操作。 **优点**: - 无锁并发,性能较高。 **缺点**: 1. **ABA问题**: - 如果一个线程读取到的值为A,然后另一个线程将该值改为B,再改回A,原线程在进行CAS操作时会认为值没有变化。 - 解决方案:使用`AtomicStampedReference`,通过版本号来判断值是否真正变化。 2. **循环时间长开销大**: - 在高竞争情况下,CAS可能需要多次尝试,会浪费大量CPU资源。 3. **只能保证一个共享变量的原子操作**: - 对于多个共享变量的操作,CAS无法保证原子性,需使用锁机制。 **ABA问题示例**: ```java import java.util.concurrent.atomic.AtomicStampedReference; public class ABAProblemSolution { private final AtomicStampedReference<Integer> atomicStampedRef = new AtomicStampedReference<>(100, 0); public void updateData() { int[] stampHolder = new int[1]; Integer currentValue = atomicStampedRef.get(stampHolder); int newStamp = stampHolder[0] + 1; atomicStampedRef.compareAndSet(currentValue, 101, stampHolder[0], newStamp); } } ``` ### 总结 - **悲观锁**:适用于写操作多的场景,通过加锁来保证数据一致性,常见实现有数据库锁和Java中的`synchronized`。 - **乐观锁**:适用于读操作多的场景,通过版本号机制或CAS实现,提升系统吞吐量。 - **CAS**:无锁并发,性能高,但存在ABA问题和循环开销大等缺点,适用于单一共享变量的原子操作。 通过合理选择锁机制,可以有效提升并发程序的性能和可靠性。
    发布于 2024-07-23 15:35 •IP属地 浙江
    举报
    评论
    收藏