原创

java面试题-乐观锁与悲观锁是什么?

引言

在并发编程中,为了确保共享数据的一致性和正确性,锁的使用是不可避免的。乐观锁和悲观锁是两种不同的锁策略,它们分别适用于不同的并发场景。本教程将深入研究乐观锁和悲观锁的概念、实现原理、适用场景以及通过示例代码演示它们的应用。

1. 悲观锁(Pessimistic Lock)

悲观锁是一种悲观地认为冲突一定会发生的锁策略。在处理共享数据时,线程每次都会上锁,其他线程在想要处理相同数据时会被阻塞,直到获取锁为止。常见的悲观锁实现包括使用synchronized关键字。

1.1 悲观锁示例代码

public class PessimisticLockExample {
    private int sharedData = 0;

    // 使用悲观锁,synchronized关键字
    public synchronized void updateSharedData() {
        // 处理共享数据的业务逻辑
        sharedData++;
    }
}

在上述代码中,synchronized关键字确保了对updateSharedData方法的互斥访问,即一次只能有一个线程执行该方法,从而保证了共享数据的一致性。

2. 乐观锁(Optimistic Lock)

乐观锁是一种基于乐观假设的锁策略。在处理共享数据时,线程每次不会上锁,而是通过数据的版本号等机制判断其他线程是否更新了数据。最典型的乐观锁实现是使用CAS(Compare-and-Swap)操作。

2.1 乐观锁示例代码

import java.util.concurrent.atomic.AtomicInteger;

public class OptimisticLockExample {
    private AtomicInteger sharedData = new AtomicInteger(0);

    public void updateSharedData() {
        int currentVersion = sharedData.get();
        int newVersion = currentVersion + 1;

        // 使用CAS操作更新数据
        if (sharedData.compareAndSet(currentVersion, newVersion)) {
            // 更新成功
            System.out.println("Data updated successfully");
        } else {
            // 更新失败,可能有其他线程已经更新了数据,进行重试或其他处理
            System.out.println("Data update failed. Retry or handle accordingly.");
        }
    }
}

在上述代码中,compareAndSet方法是CAS操作的一种,用于判断当前版本与期望版本是否一致,如果一致,则更新数据。如果不一致,说明有其他线程已经更新了数据,需要进行重试或其他处理。

3. 乐观锁与悲观锁的优缺点比较

3.1 乐观锁的优点

  • 适用于读多写少的场景,可以省去频繁加锁、释放锁的开销,提高吞吐量。
  • 在无冲突的情况下,不会发生线程阻塞,减少线程切换的开销。

3.2 乐观锁的缺点

  • 在写比较多的场景下,由于版本不一致,可能会产生大量的重试和自旋,导致CPU消耗增加,性能下降。

3.3 悲观锁的优点

  • 适用于写多的场景,通过互斥访问保证了数据的一致性,避免了冲突。
  • 不会产生重试和自旋,减少CPU消耗。

3.4 悲观锁的缺点

  • 在读多的场景下,频繁加锁、释放锁的开销

较大,可能成为性能瓶颈。

总结

乐观锁和悲观锁是并发编程中常用的两种锁策略,各有优缺点。选择合适的锁策略取决于应用场景的读写比例以及对性能的要求。在实际应用中,有时也可以根据具体情况采用两者的结合策略,以兼顾各自优势。

正文到此结束
本文目录