Java并发编程:悲观锁与乐观锁的深度解析

169 阅读2分钟

一、核心思想:心态决定机制

悲观锁乐观锁是两种处理并发访问的策略,它们的核心区别在于对并发冲突的看法。

  • 悲观锁(Pessimistic Locking)

    • 心态:认为并发冲突是常态,每次操作前都假设会发生冲突。
    • 机制:在数据被访问前,强制加锁,阻止其他线程访问。
    • 实现synchronizedReentrantLock
  • 乐观锁(Optimistic Locking)

    • 心态:认为并发冲突是小概率事件,允许并发访问。
    • 机制:在更新数据时,检查数据是否被其他线程修改过。如果被修改,则放弃本次更新或进行重试。
    • 实现CAS(Compare-And-Swap)、版本号机制。

二、Java中的具体实现

1. 悲观锁

  • synchronized:Java 内置的关键字,可以修饰方法或代码块。它是一个重量级锁,能够保证原子性、可见性和有序性
  • ReentrantLockjava.util.concurrent 包下的类,提供了比 synchronized 更灵活的锁控制。它支持可中断锁、超时锁公平锁

2. 乐观锁

  • CASCAS 是一种无锁算法,它依赖于 CPU 的原子指令,可以在不加锁的情况下实现线程安全的数据更新。

    • 工作流程CAS 包含三个参数:V(内存值)、A(旧的预期值)、B(新的值)。只有当 V 等于 A 时,才会将 V 更新为 B
    • 实现AtomicIntegerAtomicLong 等原子类。
  • 版本号机制:在数据库中,通过为数据添加一个版本号字段。每次更新数据时,都会检查版本号是否一致,并将其加 1。如果版本号不一致,则说明数据已被修改,更新失败。


三、优缺点与选型指南

维度悲观锁乐观锁
性能高并发写时性能差,锁竞争开销大。高并发读时性能高,无锁。
数据安全强一致性,避免脏数据。可能需重试,需处理冲突。
适用场景写操作多,冲突频繁,需要强一致性的场景。读操作多,冲突较少,对性能要求高的场景。
实现复杂度简单。需处理冲突重试、ABA问题等。

结论

悲观锁和乐观锁没有绝对的优劣之分,它们是两种不同的并发处理思想。在实际开发中,应根据业务场景(读写比例、一致性要求)进行权衡和选择。