说说Java 多线程之悲观锁与乐观锁

请您说说悲观锁,和乐观锁的区别

一、悲观锁


总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronizedReentrantLock等独占锁就是悲观锁思想的实现。

二、乐观锁


总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

三、两种锁的使用场景


从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。

四、乐观锁常见的两种实现方式


乐观锁一般会使用版本号机制或CAS(Compare-and-Swap,即比较并替换)算法实现。

什么是CAS?

1.比较并交换(先比较后交换成功,有点类似SVN提交前先检查是否需要更新)
2.CAS底层原理?如果知道,谈谈你对UnSafe的理解
        atomicInter.getAndIncrement();
        UnSafe
        CAS是什么
            CAS的全称为Compare-And-Swap,它是一条CPU并发原语。
            它的功能是判断内存某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的。
            CAS并发原语体现在JAVA语言中就是sun.misc.Unsafe类中的各个方法。调用
            编指令。这是一种完全依赖于硬件的功能,通过它实现了原子操作。再次强调,由于CAS是一种系
            范筹,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执
            是说CAS是一条CPU的原子指令,不会造成所谓的数据不一致问题。
           unsafe.getAndAddInt
            底层汇编
                Unsafe类中的compareAndSwapInt,是一个本地方法,该方法的实
                1.Unsafe
                    是CAS核心类,由于Java方法无法直接访问底层系统,需要能过本地(na
                    可以直接操作特定内存的数据。Unsafe类存在于sun.misc包中,其
                    CAS操作的执行依赖于Unsafe类的方法。
                    注意Unsafe类中的所有方法都是native修饰的,也就是说Unsaf
                2.变量valueOffset,表示该变量值在内存中的偏移地址,因为Unsafe
                3.变量value用volatile修饰,保证了多线程之间的内存可见性。
            简单版小总结
                CAS(CompareAndSwap)
                比较当前工作内存中的值和主内存中的值,如果相同则执行规定操作,
                否则继续比较直到主内存和工作内存中的值一致为止。
                CAS应用
                CAS有3个操作数,内存值V,旧的预期值A,要修改的更新值B。
                当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
3.CAS缺点(自旋锁)
        循环时间长开销很大
            我们可以看到getAndInt方法执行时,有个do while,如果CAS失败,会一直进
            如果CAS长时间一直不成功,可能会给CPU带来很大的开销。
        只能保证一个共享变量
        引出来ABA问题,怎样解决ABA?
            CAS会导致"ABA问题"   理解原子引用 + 新增一种机制,那就是修改版本号(类似时间戳)

五、乐观锁的缺点


ABA 问题是乐观锁一个常见的问题。

  1. ABA 问题
  2. 循环时间长开销大
  3. 只能保证一个共享变量的原子操作

六、CAS与synchronized的使用情景


简单的来说CAS适用于写比较少的情况下(多读场景,冲突一般较少),synchronized适用于写比较多的情况下(多写场景,冲突一般较多)

  • 对于资源竞争较少(线程冲突较轻)的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源;而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。
  • 对于资源竞争严重(线程冲突严重)的情况,CAS自旋的概率会比较大,从而浪费更多的CPU资源,效率低于synchronized。

补充: Java并发编程这个领域中synchronized关键字一直都是元老级的角色,很久之前很多人都会称它为 “重量级锁” 。但是,在JavaSE 1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的 偏向锁 和 轻量级锁 以及其它各种优化之后变得在某些情况下并不是那么重了。synchronized的底层实现主要依靠 Lock-Free 的队列,基本思路是 自旋后阻塞,竞争切换后继续竞争锁,稍微牺牲了公平性,但获得了高吞吐量。在线程冲突较少的情况下,可以获得和CAS类似的性能;而线程冲突严重的情况下,性能远高于CAS。


其他精彩文章链接:

 


文章最后,给大家推荐一些受欢迎的技术博客链接

  1. Hadoop相关技术博客链接
  2. Spark 核心技术链接
  3. JAVA相关的深度技术博客链接
  4. 超全干货--Flink思维导图,花了3周左右编写、校对
  5. 深入JAVA 的JVM核心原理解决线上各种故障【附案例】
  6. 请谈谈你对volatile的理解?--最近小李子与面试官的一场“硬核较量”
  7. 聊聊RPC通信,经常被问到的一道面试题。源码+笔记,包懂

 


欢迎扫描下方的二维码或 搜索 公众号“10点进修”,我们会有更多、且及时的资料推送给您,欢迎多多交流!

                                           

       

 

你可能感兴趣的:(java,java,面试)