乐观锁和悲观锁

概述

曾在面试时,被问及锁的问题,当时只是模糊的描述了一下,事实证明基础知识还是不能太薄弱。今天就来恶补一下乐观锁和悲观锁。

悲观锁

锁如其名,就是很悲观,总是考虑最坏的情况,认为这次更新都可能会有其他人也会进行更新,所以会先锁住,然后再进行读写操作,在它释放锁之前任何人都不能对其进行读写操作。所以其他人想要操作会被阻塞,知道获取到锁。锁机制有:行锁,表锁,读锁,写锁等。Java中synchronizedReentrantLock等独占锁就是悲观锁思想的实现。

乐观锁

锁如其名,就是很乐观,总是假设最好的情况,每次读写数据的时候都不会上锁,而是通过版本号机制或者CAS算法来控制数据被其他人更新的问题。

  1. 版本号机制

    数据表中增加一个version版本号字段,每次数据更新版本号都会加一。当修改数据库数据时,会先判断数据库中的版本号和你之前的版本号是否一致,如果一致才更新成功,否则整体重新再来一遍,直到成功为止。

  2. CAS算法

    compare and swap(比较与交换),是一种有名的无锁算法,更多的应用于内存模式。CAS涉及到三个操作数:需要读写的内存值V,进行比较的值A,拟写入的新值B。仅当V值等于A值,B值才能进行原子操作更新V值,否则不进行操作。一般情况下是一个自旋操作,即不断的重试

乐观锁的缺点
  1. ABA问题:如果变量V,读取的时候是A值,在赋值的时候检查它仍然是A值,这个时候我们认为它从未改变过,显然是不对的。因为它可能被其他线程从A变成B,又从B变回A了,而CAS操作会认为它未被修改过,这就是CAS操作的ABA问题。但是jdk1.5解决了该问题
  2. 自旋CAS,如果长时间不成功,会给CPU带来非常大的执行开销。

使用场景

  1. 对于资源竞争严重(线程冲突严重)的情况,建议用synchronized,悲观锁。
  2. 对于资源竞争较少(线程冲突较轻)的情况,建议用CAS,乐观锁。