悲观锁(Pessimistic Lock)
悲观锁,具有强烈的独占和排他特性。
1️⃣理解
当要对数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发。这种借助数据库锁机制,在修改数据之前先锁定,再修改的方式被称之为悲观并发控制【Pessimistic Concurrency Control,缩写“PCC”,又名“悲观锁”】。
悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。
- 传统的关系型数据库使用这种锁机制,比如行锁、表锁、读锁、写锁等,都是在操作之前先上锁。
- Java 里面的同步 synchronized 关键字的实现。
乐观锁(Optimistic Locking):
1️⃣理解
乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果冲突,则返回给用户异常信息,让用户决定如何去做。乐观锁适用于读多写少的场景,这样可以提高程序的吞吐量。
版本号控制:一般是在数据表中加上一个数据版本号 version 字段,表示数据被修改的次数。当数据被修改时,version 值会 +1。当线程 A 要更新数据时,在读取数据的同时也会读取 version 值,在提交更新时,若刚才读取到的 version 值与当前数据库中的 version 值相等时才更新,否则重试更新操作,直到更新成功。
乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。
以 MySql Innodb 引擎举例:
悲观锁的使用:
乐观锁实现方式:
3️⃣并发环境下 count++ 不安全问题的解决方案
方案①:synchronized 加锁。同一时间只有一个线程能加锁,其他线程需要等待锁,这样就不会出现 count 计数不准确的问题了:
public class Increment {
private int count = 0;
public synchronized void add() {
count++;
}
}
理解 CAS 底层: