悲观锁和乐观锁的概念
乐观锁和悲观锁在面试过程中是经常遇到的,那么什么是乐观锁什么是悲观锁呢?首先需要明确的是乐观锁和悲观锁是两种思想,跟编程语言无关,任何语言都有对这两种思想的实现。下面我们来分别谈谈这两种思想。
乐观锁的概念及实现机制
乐观锁是指在操作数据的时候保持乐观态度,即数据不会被别人修改。因此乐观锁有个很明确的特点是乐观锁其实并不加锁。乐观锁只是在修改数据时判断是否在数据获取后到提交修改这期间被别人修改,如果不存在修改则执行自己的操作,否则放弃操作。
乐观锁实现的手段通常有2种,一种是CAS,一种是利用版本号实现。
CAS及Compare And Swap,是一种无锁算法,一般接触CAS是因为i++操作。CAS操作可以保证在无锁状态下i++的原子性。CAS算法主要有3个参数,内存偏移量offset,期望值A和待修改值B。当且仅当offset处的值与A相同时,修改为B,否则不进行任何操作。一般情况下外部会进行自旋不断尝试。
版本号的方式比较容易理解,在数据库种有大量应用,即给数据加上一个version字段,每次操作时判断提取出来的数据版本和此时的版本是否相同,相同则执行操作数据并将version+1,否则不进行操作。
悲观锁的概念及实现机制
悲观锁是指在操作数据的时候持悲观态度,即数据会被他人修改,因此在每次操作前会对数据加锁,直到操作结束时释放锁,在此期间其他人不能操作数据。
悲观锁的实现方式是对代码块加锁(如Synchronized或ReentrantLock等)
优缺点
知道了这两种锁的概念和实现机制后我们来分析分析各自的优缺点,其实两种锁并没有优劣之分,只是看更适合的场景。
由于乐观锁是采用CAS或版本号机制实现,因此存在一些限制,例如
- CAS操作只能针对一个变量来操作,对多变量的修改则无法操作。
- 还有CAS操作带来的A-B-A问题,即在线程1获取数据是变量值为A,在线程1处理的过程中有其他线程对数据进行了修改使变量值成了B,此后又有其他操作将变量改回了A,此时线程1操作结束提交修改时发现变量值仍然是A,则会提交操作。
- CAS一般会配合自旋一起使用,在CAS操作失败后长时间自旋会带来非常大的CPU开销。
另外版本号机制也存在其他问题,例如
- 多数据源只间同步数据时,两方数据版本都需要控制,否则任何一方的版本控制都是没意义的。
- 此外,由于乐观锁并不对数据加锁,因此可能出现第三方系统修改了数据而我们不知道的情况。
悲观锁由于会对数据加锁,线程获取锁之后其他线程无法获取锁,若操作耗时较长时势必会影响吞吐量。
总结
通过上面的分析可以看出,乐观锁适合读多的场景,而悲观锁适合写多的场景。即对资源竞争激烈的情况下,导致CAS自旋的概率很高,适合使用悲观锁。在资源竞争较少的情况,加锁会浪费CPU资源,而乐观锁可以获得更高的性能。
网友评论