乐观锁和悲观锁

1.乐观锁:

        乐观锁假设数据冲突的可能性较小,因此不加锁,而是在更新数据时检查数据是否被其他事务修改过。乐观锁通常基于数据版本(Version)或时间戳(Timestamp)来实现。

       (1). 实现方式:每次读取数据时,读取其版本号(或时间戳),在更新数据时,检查当前版本号是否与数据库中的版本号一致。如果一致,则更新成功;否则,说明数据已被其他事务修改,操作失败,需要重试。

       (2). 优点:不会产生锁等待,因此能提高系统的吞吐量和响应速度。
适用于读多写少的场景,冲突较少的情况下性能较高。
       (3). 缺点:在写操作频繁的场景下,冲突概率较高,可能导致大量重试,影响性能。

2.CAS:

        CAS(Compare and Swap,即比较并交换)是一种用于实现多线程并发的无锁操作机制,常用于解决并发问题,确保多线程环境下的数据一致性。CAS操作在计算机硬件层面上被广泛支持,是乐观锁的实现基础之一

        2.1 什么是CAS?:

       CAS是一种原子操作,它涉及三个操作数:

        (1).内存地址V:需要更新的变量的内存地址。
        (2).期望值A:期望变量的当前值。
        (3).新值B:希望将变量更新的新值。

       2.2 CAS操作的工作原理

  • 当多个线程尝试更新同一个变量时,CAS会检查当前变量的值是否与期望值(A)相同。
  • 如果相同,则将变量的值更新为新值(B)。
  • 如果不同,则表示其他线程已经修改过这个变量,操作失败,线程可以选择重试或其他操作。

       2.3 CAS的优点:

        (1).无锁操作:CAS是一种无锁机制,不需要对数据进行锁定,因此避免了传统锁机制可能产生的锁竞争、死锁等问题。

        (2).高性能:由于CAS操作是由硬件层面的指令支持的(如x86处理器中的cmpxchg指令),因此操作非常快速,适用于高并发环境。

        2.4 CAS的缺点

        (1).ABA问题:CAS在比较旧值和当前值时,如果一个变量从A变为B,再变回A,CAS无法检测到这个变化。这种情况被称为ABA问题。解决ABA问题的一种方法是引入版本号或时间戳,每次更新时版本号+1,从而避免误判断。

        (2).自旋导致的高开销:当多线程不断重试操作时,可能会导致CPU资源的浪费。尤其是在高并发的场景下,如果多个线程一直自旋等待,会导致CPU负载过高。

        (3).只能保证一个共享变量的原子操作:CAS只能对一个变量进行操作,如果需要对多个变量进行原子操作,CAS无能为力。这时候需要更复杂的同步机制,如锁或事务。

        2.5 CAS的应用场景

CAS通常用于实现无锁的数据结构和算法,如:

  • Atomic类:Java中的AtomicIntegerAtomicLongAtomicReference等类底层都是基于CAS操作实现的原子性操作。
  • Concurrent包中的容器:Java的ConcurrentHashMap等容器在实现线程安全时也使用了CAS操作。
  • 自旋锁和无锁队列:一些高效的并发工具中也利用CAS来实现。

3.悲观锁

        悲观锁假设数据冲突的可能性较大,因此对数据的操作采取“悲观”态度,读取数据时直接加锁,以防止其他事务修改数据。

        (1).实现方式:在读取数据时对其加锁(通常是行锁或表锁),其他事务在试图访问被锁定的数据时必须等待锁释放。

        (2).优点:能有效避免并发冲突,保证事务的安全性。适用于写多读少的场景,冲突较多的情况下性能较好。

        (3).缺点:会产生锁等待,可能导致死锁、锁竞争等问题,影响系统的吞吐量和响应速度。

4.适用场景:

  • 乐观锁适合于并发冲突少的应用场景,如大多数读取操作,较少写入的场景。
  • 悲观锁适合于并发冲突多的应用场景,如频繁的写操作或事务处理。

5.乐观锁与悲观锁的主要区别:

区别主要在于对并发控制的假设和处理方式:

  • 乐观锁:假设冲突很少发生,不在操作前加锁,而是操作完成后通过版本号或时间戳来检测是否有冲突,适用于读多写少的场景。

  • 悲观锁:假设冲突经常发生,操作前加锁,确保同一时刻只有一个事务能操作数据,适用于写多冲突高的场景。

        乐观锁提高了并发性能但可能需要重试;悲观锁降低了冲突风险但会增加等待和锁的开销。

6.总结:

        两种锁机制各有优缺点,应根据具体应用场景和业务需求选择合适的并发控制策略。

你可能感兴趣的:(java,数据库,开发语言)