关于锁,你真的了解吗?

并发场景下会出现数据安全问题,我们经常通过加锁来保证数据的一致性,锁分为乐观锁和悲观锁

乐观锁

顾名思义,乐观锁比较乐观,假设冲突不会发生,但是在更新时会判断其他线程在这之前有没有对数据进行修改。实现方式有两种,分别是版本号法和CAS(Compare And Swap/Compare And Set)

版本号法

实现原理:

  • 在数据库表中增加一个“version”字段,每次读取数据时都会获取当前版本号。
  • 在更新数据时,将此版本号作为更新条件之一,检查当前版本号是否与读取时一致。
  • 如果版本号一致,则执行更新操作,并将版本号加一;如果不一致,则说明在此期间有其他事务对数据进行了更新,当前更新操作应失败并可能需要重新读取数据尝试更新。

优点:

  • 并发性能较高,不需要阻塞其他事务的执行。
  • 实现简单,易于理解和维护。

缺点:

  • 在高冲突率的场景下,可能需要频繁地回滚和重试,影响性能。

CAS

实现原理:

  • CAS是一种无锁算法,通过比较并交换的方式实现对共享变量的原子更新。
  • CAS操作包括三个操作数:被操作的内存值V、预期的值A和新值B。
  • 当且仅当V的值等于A时,CAS才会将V的值更新为B,否则不进行任何操作。

优点:

  • 无需加锁,避免了锁带来的开销和复杂性。
  • 适用于高并发场景,能够提高系统性能。

缺点:

  • 在高冲突率的场景下,可能会因为频繁的重试而导致性能下降。
  • 需要硬件或特定编程语言的支持(如Java中的Atomic类)。

存在的问题及解决方案

  • 循环时间太长和CPU开销:乐观锁通常通过自旋锁(spin lock)或CAS(Compare-And-Swap)操作来实现。在CAS操作中,如果数据在准备修改时被其他线程修改,则当前线程会重新尝试,这可能导致长时间的自旋等待。如果自旋时间过长且不成功,将大大增加CPU的开销。

    • 解决方案:限制自旋次数,避免无限制的自旋等待,以减少CPU的浪费。
  • 只能保证一个共享变量的原子操作:CAS操作通常只能保证对一个共享变量的原子性修改。如果需要对多个变量进行原子性操作,CAS则无法满足需求。

    • 对于需要同时修改多个变量的场景,可以考虑使用其他同步机制,如悲观锁或更复杂的无锁算法。
  • ABA问题:一个共享变量在某个时间点的值是A,然后经过一系列操作后又变回了A,但在这个过程中,它的值可能已经被其他线程修改成了B(或其他值)。如果只检查变量的值是否相同,就会忽略掉中间的变化,从而导致数据不一致的问题。

    • 解决方案:使用版本号或时间戳来记录变量的变化历史,从而避免ABA问题。在每次修改时,除了检查变量的值外,还需要检查版本号或时间戳是否一致。

悲观锁

悲观锁比较"悲观",假设冲突一定会发生,通过加(悲观)锁来确保某一时刻只有一个线程能读取并修改数据。常见的悲观锁有Java中的synchronized关键字,数据库中的表锁、行锁、页锁等等

乐观锁与悲观锁对比

乐观锁和悲观锁各有其适用场景和效率表现,不能一概而论地说乐观锁一定比悲观锁效率高。

乐观锁的效率

乐观锁通常适用于读多写少的场景,其核心思想是假设冲突不会频繁发生,只在数据更新时检查是否有冲突发生。如果没有冲突,则直接更新数据,避免了加锁的开销;如果有冲突,则可能需要重试更新操作。在冲突较少的情况下,乐观锁可以显著提高系统的吞吐量,因为它减少了锁的使用,降低了锁的竞争和等待时间。

悲观锁的效率

悲观锁则适用于写操作较多或冲突频繁的场景。它假设冲突总是会发生,因此在数据访问前就先加锁,以确保数据的一致性和完整性。悲观锁通过锁机制来避免冲突,但它也带来了额外的开销,包括锁的获取、释放和可能的锁等待时间。在高冲突的场景下,悲观锁可以确保数据的一致性,但可能会降低系统的吞吐量。

比较

  • **在高冲突场景下,悲观锁可能更有效率。**因为它通过锁机制直接阻止了冲突的发生,避免了因冲突而导致的重试和回滚操作。
  • **在低冲突场景下,乐观锁可能更有效率。**因为它避免了不必要的锁开销,减少了锁的获取、释放和等待时间。

结论

因此,在选择乐观锁还是悲观锁时,需要根据具体的应用场景、系统负载、事务大小、冲突频率以及数据库和存储引擎的支持情况等因素进行综合考虑。在某些情况下,乐观锁可能比悲观锁更有效率,但在其他情况下则可能相反。

和解操作是在多线程编程中用于控制对共享资源的访问,以避免线程之间的竞争和冲突。在多线程编程中,如果多个线程同时访问共享资源,可能会导致数据出现异常或不一致的情况。 Sync的原理: Sync是一种同步机制,用于控制多个线程之间的同步操作。它是通过互斥的方式来实现的。当多个线程同时访问同一个共享资源时,sync会阻止其他线程的访问,直到当前线程完成对共享资源的操作并释放为止。 Sync的实现原理是基于操作系统提供的互斥(Mutex)机制。当一个线程需要访问共享资源时,它会先尝试获取互斥。如果互斥已经被其他线程获取了,则当前线程会被阻塞,直到其他线程释放了互斥。当当前线程获取到互斥后,它就可以执行对共享资源的操作,并在操作完成后释放互斥。 Lock的原理: Lock是一种机制,与Sync的实现原理类似,也是通过互斥的方式来实现的。Lock的主要作用是保护共享资源,避免多个线程同时访问共享资源而导致的数据冲突和不一致的情况。 Lock的实现原理也是基于操作系统提供的互斥机制。当一个线程需要访问共享资源时,它会先尝试获取。如果已经被其他线程获取了,则当前线程会被阻塞,直到其他线程释放了。当当前线程获取到后,它就可以执行对共享资源的操作,并在操作完成后释放。 相比Sync,Lock提供了更多的功能和灵活性。Lock可以设置的类型、的超时时间、的公平性等。因此,在某些情况下,使用Lock比使用Sync更加合适。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值