乐观锁

每个线程在操作共享资源之前都不会对资源进行加锁,只有在更新资源的时候才会进行冲突检测。乐观锁的实现机制通常是通过版本号和时间戳来追踪共享资源的状态变化。

当一个线程要更新资源时,会获取当前资源的版本号,检查当前版本号是否发生了变化,如果发生变化更新失败,没有发生变化就可以更新成功。乐观锁通常用于读取操作比写入操作频繁的场景

通过版本控制,比如说:

在数据库中有一个version字段和一个余额字段,A、B两个线程要修改余额,分别修改余额后,比如A线程开始提交,提交完后version+1,接下来B线程进行提交的时候,发现version和数据库中的version不一样,就会提交失败

悲观锁

每个线程在操作共享资源之前都会对该资源进行加锁,其他线程无法访问。悲观锁的实现机制通常是加互斥锁,如synchronized关键字。

当一个线程要访问该资源时,会获得该资源的锁,其他线程必须要等该锁释放才能访问资源。

悲观锁通常用于写入操作比读取操作较频繁的场景。

CAS算法

就是用一个预期值和要更新的变量值进行比较,两值相等才会进行更新。

V:要更新的变量值

E:预期值

N:拟写入的新值

CAS算法举例:

线程A要修改一个变量i的值,将它修改为6,i的原值为1,这是V=1,E=1,N=6

此时A要修改的话,会将i与1进行比较,如果相等,则表示没有被其他线程修改,可以被设置为6;如果不相等,说明被其他线程修改,当前线程放弃修改,CAS操作失败。(假设不存在ABA)

Java并没有直接实现CAS,是通过c++内联汇编的的形式实现的。所以他和操作系统及CPU都有关系。它是一个原子操作,底层依赖于一条CPU的原子指令。原子操作就是最小的、不可拆分的操作,也就说操作一旦开始,就不能被打断,直到操作完成。

缺点:

ABA问题

某个线程读取某个值的为A,然后在准备赋值的时候检查它的值仍然是A,那么这个时候是不能保证这个值没有被更改的···

循环时间开销大

CAS经常会用到自旋操作来进行重试,也就是不成功就一直循环执行直到成功,如果长时间不成功,会给CPU带来很大的开销。

参考博客