乐观锁 VS 悲观锁
Posted 小刘你最强
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了乐观锁 VS 悲观锁相关的知识,希望对你有一定的参考价值。
乐观锁 VS 悲观锁
悲观锁:总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁、写锁、行锁等),当其他线程想要访问数据时,都需要阻塞挂起。
乐观锁:总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改。
乐观锁在Java中通过使用无锁来实现,常用的是CAS,Java中原子类的递增就是通过CAS自旋实现。
CAS
CAS全称 Compare And Swap(比较与交换),是一种无锁算法。在不使用锁(没有线程被阻塞)的情况下实现多线程之间的变量同步。java.util.concurrent包中的原子类就是通过CAS来实现了乐观锁。
一个 CAS 涉及到以下操作:
我们假设内存中的原数据V,旧的预期值A,需要修改的新值B,
- 比较 A 与 V 是否相等。(比较)
- 如果比较相等,将 B 写入 V。(交换)
- 返回操作是否成功。
CAS的底层原理
- 调用 Unsafe 类中的 CAS 方法,JVM 会帮我们实现出 CAS 汇编指令
- 这是一种完全依赖于硬件的功能,通过它实现原子操作
- 原语的执行必须是连续的,在执行过程中不允许被中断,CAS 是 CUP 的一条原子指令
CAS的三大问题
- 如果 CAS 长时间一直不成功,会给 CPU 带来很大的开销,在Java的实现中是一只通过while循环自旋CAS获取锁。
- 只能保证一个共享变量的原子操作
- 引出了 ABA 问题
ABA问题
CAS需要在操作值的时候检查内存值是否发生变化,没有发生变化才会更新内存值。但是如果内存值原来是A,后来变成了B,然后又变成了A,那么CAS进行检查时会发现值没有发生变化,但是实际上是有变化的。
如何解决ABA问题
加入版本信息,例如携带 AtomicStampedReference 之类的时间戳作为版本信息,保证不会出现老的值。
UnSafe
Unsafe类是在sun.misc包下,不属于Java标准。但是很多Java的基础类库,包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的,比如Netty、Cassandra、Hadoop、Kafka等。Unsafe类在提升Java运行效率,增强Java语言底层操作能力方面起了很大的作用。
使用Unsafe可用来直接访问系统内存资源并进行自主管理,Unsafe类在提升Java运行效率,增强Java语言底层操作能力方面起了很大的作用。
Unsafe可认为是Java中留下的后门,提供了一些低层次操作,如直接内存访问、线程调度等。
这个类的提供了一些绕开JVM的更底层功能,基于它的实现可以提高效率。但是,它是一把双刃剑:正如它的名字所预示的那样,它是Unsafe的,它所分配的内存需要手动free(不被GC回收)。如果对Unsafe类理解的不够透彻,就进行使用的话,就等于给自己挖了无形之坑,最为致命。
以上是关于乐观锁 VS 悲观锁的主要内容,如果未能解决你的问题,请参考以下文章
这年头,连“锁”都开始有情绪了。。乐观锁 VS 悲观锁(CAS)