什么是乐观锁和悲观锁
悲观锁,顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。悲观锁适用于并发竞争很厉害,写比较多的操作。
乐观锁,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。乐观锁适用于读多写少的应用场景,这样可以提高吞吐量。
悲观锁的实现
数据库排它锁、行锁、表锁、读锁、写锁、共享锁。
排它锁(下面这个例子只给一条数据加了锁,也叫行锁):当一个事物加入排他锁后,不允许其他事务加共享锁或者排它锁读取,更加不允许其他事务修改加锁的行。
select * from account where name=”xxx” for update
共享锁:允许不同事务之前共享加锁读取,但不允许其它事务修改或者加入排他锁。如果有修改必须等待一个事务提交完成,才可以执行,容易出现死锁。
- 允许其它事务也增加共享锁读取
- 不允许其它事物增加排他锁(
for update
) - 当事务同时增加共享锁时候,事务的更新必须等待先执行的事务commit后才行,如果同时并发太大可能很容易造成死锁
select * from account where name=”xxx” lock in share mode
Java中Synchronized和ReentrantLock
乐观锁的实现
数据库的版本号机制(或者时间戳)
update account set price=100,version=2 where version=1
Java中的CAS(compare and swap)算法实现。CAS算法涉及到三个操作数:
- 需要读写的内存值 V
- 进行比较的值 A
- 拟写入的新值 B
当且仅当 V 的值等于 A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试。CAS需要考虑数据变化“ABA”的问题,这时可以检测是否被改过。(参见java.util.concurrent.atomic.AtomicStampedReference )