乐观锁和悲观锁

乘着风

锁机制

在数据库操作过程中,为了避免两个或多个用户同时对一条数据操作,通常采用锁的机制来来解决数据冲突问题。
同样,在程序流程中为了避免对多线程共享的资源的修改冲突,也采用锁的机制来避免修改冲突

锁的分类

乐观锁(Optimistic Lock)

所谓乐观锁,就是相信大部分场景下,不会产生数据修改冲突,所以在读取数据进行修改的时候,不对数据进行加锁,而是在最终提交修改的时候,通过version或CAS机制,检查对数据的修改是否发生了冲突。
乐观锁的适用于读多写少的场景,能提供系统的处理能力,如果在冲突比较概率高的场景使用乐观锁,反而会降低系统的处理能力。

悲观锁(Pessimistic Lock)

所谓悲观锁,就是认为对数据修改发生冲突的概率比较大,所以在读取数据进行修改的时候,先用“排他写锁”锁住数据,Block其他人的操作,等修改完成后,再释放锁。
此模式比较适用于数据修改冲突发生概率高的场景,但会一定程度降低系统的处理能力。

数据库场景

悲观锁

数据的行锁、表锁、读锁、写锁都属于悲观锁,典型的就是select * from xxx where id=n for update命令。

乐观锁

CAS机制(compare and swap)

假设有一条订单(order)数据,ID为1,订单状态(status)是已付款,这个时候,商家打开订单列表,准备进行发货;在商家打开订单后,这时候用户在APP端取消了订单,但是商家不知道;商家执行发货的时候;这时候商家操作发货,如果只根据ID进行更新:

update order set status='已发货' where id=1

则会导致取消的订单被发货,此时,使用CAS机制,在更新数据的时候检查订单状态是否正确:

update order set status='已发货' where id=1 and status='已付款'

并通过检查update语句发返回值,可以确认时数据更新是否成功。

Version机制

Version机制,是在order表中增加一个数字型的version字段,每次查下数据的时候,都带上version字段。更新数据是,把version字段加1。以上述订单为例,比如:

  1. 订单创建后version为1;
  2. 付款后version为2;
  3. 此时商家准备发货,读到的version为2;
  4. 用户取消订单后,version为3;
  5. 商家发货是,更新订单状态是,发现version不是读取时的2,说明订单已经被更新,系统驳回商家的修改,并提升商家。

JAVA锁场景

Java中, java.util.concurrent.atomic包下的原子变量属于使用CAS计算的乐观锁。

public class AtomicInteger extends Number implements java.io.Serializable { 
  private volatile int value; 
 
  public final int get() { 
    return value; 
  } 
 
  public final int getAndIncrement() { 
    for (;;) { 
      int current = get(); 
      int next = current + 1; 
      if (compareAndSet(current, next)) 
        return current; 
    } 
  } 
 
  public final boolean compareAndSet(int expect, int update) { 
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update); 
  } 
}

getAndIncrement 采用了CAS机制,每次从内存中读取数据,然后将此数据和 +1 后的结果进行CAS操作,如果成功就返回结果,否则重试直到成功为止。
compareAndSet 利用JNI来完成CPU指令的操作:

public final boolean compareAndSet(int expect, int update) {  
  return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
} 

unsafe.compareAndSwapInt(this, valueOffset, expect, update)逻辑类似如下:

if (this == expect) {
   this = update
   return true;
 } else {
   return false;
 }

synchronized关键字属于悲观锁。

CAS的问题

ABA问题

如线程1读取了一个变量的值为A;这时候线程2修改变量的值B;线程3有把变量值改回为A;此时,线程1再去更新此变量,会认为此变量未被其他人更新过,但其实变量已经被更新了多次。
所以CAS是适用于对象子包含单个共享变量的原子操作,对于对象中包含多个共享变量的情况无法保证原子性。

锁开销

对于资源竞争比较激烈的情况,CAS自旋的概率较大,会导致CPU开销增大,效率会低于synchronized

阅读 1.2k

五岁时,妈妈告诉我,人生的关键在于快乐。上学后,人们问我长大了要做什么,我写下“快乐”。他们告诉我...

90 声望
10 粉丝
0 条评论

五岁时,妈妈告诉我,人生的关键在于快乐。上学后,人们问我长大了要做什么,我写下“快乐”。他们告诉我...

90 声望
10 粉丝
文章目录
宣传栏