• Synchronized 和 lock的区别?+悲观锁和乐观锁+可重入锁【Interview】


    目录

    1Synchronized 和 lock的区别?

    2什么是行锁?

    3悲观锁和乐观锁?

    4什么是闭锁(CountDownLatch)?

    5什么是可重入锁(Reentrant Lock)?

    6Synchronized 锁升级原理

    7Synchronized 加非静态和静态方法上的区别

    8Synchronized(this) 和 Synchronized (User.class) 的区别?

    9Synchronized 和 volatitle 关键字的区别?

    10一个类中两个方法中都有Synchronized(this) 请问能锁住吗?为什么


    1Synchronized 和 lock的区别?

    并发操作:同一时间可能有多个事务对同一数据进行读写操作

    他们都是用来解决并发编程中的线程安全问题的,不同的是

    1、lock是一个接口,Lock是显式锁加锁解锁都需要我们使用java代码实现

    而synchronized是java的一个关键字。是隐式锁,加锁解锁是JVM管理的

    2、synchronized在发生异常时会自动释放占有的锁,因此不会出现死锁;

    而lock发生异常时,不会主动释放占有的锁,必须手动来释放锁(finally中释放),可能引起死锁的发生。

    3synchronized能锁住类、方法和代码块,而Lock是块范围内

    2什么是行锁?

    行锁指的是一行数据进行加锁,行锁又分为共享锁和排他锁

    共享锁:允许事务读取一行数据,允许多个事务同时获取该锁。(反正是读取,管你怎么看)

    排他锁:允许事务更新和删除一行数据,具有互斥性,某个事务想要获取到锁,就必须等待其他事务释放操作该数据的锁。

    3悲观锁和乐观锁?

    悲观锁和乐观锁是两种思想,用于解决线程并发场景下资源竞争的问题

    • 悲观锁,每次操作数据的时候,都会认为会出现线程并发问题(会同时对数据进行修改),一开始就会进行上锁,执行完成过后才会释放锁,线程安全,性能较低,适用于写多读少的场景

    • 乐观锁,每次操作数据的时候,认为不会出现线程并发问题不会使用加锁的形式而是在提交更新数据的时候,判断一下在操作期间有没有其他线程修改了这个数据,如果有,则重新读取,再次尝试更新(Retry),循环上述步骤直到更新成功 。否则就执行操作。线程不安全,执行效率相对较高,适用于读多写少的场景

    悲观锁一般用于并发冲突概率大,对数据安全要求高的场景。【会进行加锁,不会出现线程安全问题。乐观锁在执行更新时频繁失败,需要不断重试,反而会浪费CPU资源

    乐观锁一般用于高并发,多读少写的场景【乐观锁不会进行加锁,其他线程可以进行同时访问提高响应效率

    乐观锁的使用场景

    ES是采用的乐观锁,因为ES的使用场景为读多写少,基本上不会出现线程并发问题

    4什么是闭锁(CountDownLatch)?

    闭锁的使用场景:多个线程执行完成之后才能执行某个方法。

    5什么是可重入锁(Reentrant Lock)?

    指的是某个线程已经获取到某个锁,可以再次获取锁而不会出现死锁【两个或两个以上的进程在执行过程中,因争夺系统资源而产生相互等待现象。】。再次获取锁的同时会判断当前线程是否持有锁,如果有,就对锁的次数进行加1,释放锁的时候加了几次锁,就需要释放几次锁。

    作用:可以解决多个线程对同一共享资源进行操作出现的线程并发问题,可以降低死锁问题。

    在实际业务中,比如说抢购下单,在支付超时的时候,需要退抢购的库存的同时,下单成功了,就会导致超卖的问题。

    所以采用可重入锁,在退库存的业务方法和下单的业务方法中都加上相同的Reentrant Lock,当一个业务方法获取到锁过后,另一个方法就需要等待当前锁的释放才能够进行执行,这样就避免了线程并发问题,导致数据不一致问题。

    一个简单的锁分布式锁案例如下:

    1. @Autowired
    2. private RedissonClient redissonClient;
    3. @Test
    4. public void testLock1(){
    5. RLock rLock = redissonClient.getLock("lock_stock");
    6. rLock.lock(); //阻塞式等待,过期时间30s
    7. try{
    8. //这里面写真正的业务,如果30秒内没有执行完当前业务,底层会有一个监控锁的看门狗会定时去检 //测,然后当当前锁进行续期,直到执行结束。这样做的好处是防止在程序执行期间锁自动过期被删除问题
    9. //当业务执行完成不再给锁续期,即使没有手动释放锁,锁的过期时间到了也会自动释放锁
    10. System.out.println("加锁成功....");
    11. System.out.println("执行业务....");
    12. }finally {
    13. rLock.unlock();
    14. System.out.println("释放锁....");
    15. }
    16. }

    另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了,如下

    1. @Test
    2. public void testLock2(){
    3. RLock rLock = redissonClient.getLock("lock_stock");
    4. // 加锁以后10秒钟自动解锁
    5. // 无需调用unlock方法手动解锁
    6. rLock.lock(10, TimeUnit.SECONDS);
    7. try{
    8. System.out.println("加锁成功....");
    9. System.out.println("执行业务....");
    10. }finally {
    11. rLock.unlock();
    12. System.out.println("释放锁....");
    13. }
    14. }

    6Synchronized 锁升级原理

            JDK1.6之前synchronized是基于JVM内置锁实现,通过内部对象Monitor(监视器锁)实 现监视器锁的实现依赖于底层操作系统的Mutex lock(互斥锁)实现(只有一个线程能够拿到锁,其他就需要进行等待),它是一个重量级锁性能较低,每次操作都会执行加锁。

            JDK1.6优化之后,采用锁升级的方式,就是按照1偏向锁(无锁),2轻量级锁(CAS),3重量级锁的方式来提高线程的执行效率。并不是一开始就使用重量级锁。

            锁升级主要依赖Mark World中的锁标志位。

            1在大多数情况下不会出现多线程竞争锁,而是同一个线程在多次获取锁,偏向锁的目的就是避免线程重复获取锁,降低资源开销,第一次线程获取到锁过后,会将当前线程id记录到Mark World的锁标志位中第二次还是这个线程来获取锁时,就不需要再次获取锁,降低资源开销。

            2当有少量来争夺锁的时候偏向锁就会升级为轻量级锁,此时竞争的线程不会阻塞,而是进行自旋。提高程序响应速度。

            3如果锁竞争严重,某个线程达到最大自旋次数(默认10次)的时候,轻量级锁会升级成重量级锁,这时候当没有获取到锁的线程,就会进行阻塞状态。

    7Synchronized 加非静态和静态方法上的区别

    java中synchronized的普通方法与静态方法获取的锁对象是什么_junranhuigu的博客-CSDN博客

            synchronized修饰的普通方法获取的是等效于this的指向对象的内置锁,即新创建的对象本身

            synchronized修饰的静态方法获取的是该方法所在类的内置锁,即XXX.class

           

    8Synchronized(this) 和 Synchronized (User.class) 的区别?

    synchronize(this)作用于当前对象,保证同一时间只有一个线程能调用该对象。
    synchronize(*.class)作用于该类所属的所有实例,保证同一时间只有一个线程能调用该类的实例。

    9Synchronized 和 volatitle 关键字的区别?

    这两个关键字都是用来解决并发编程中的线程安全问题的,不同点主要有以下几点

    第一:volatile的实现原理,是在每次使用变量时都必须重主存中加载,修改变量后都必须立马同步到主存;synchronized的实现原理,则是锁定当前变量,让其他线程处于阻塞状态

    第二:volatile只能修饰变量,synchronized用在修饰方法和同步代码块中

    第三:volatile修饰的变量,不会被编译器进行指令重排序,synchronized不会限制指令重排序

    第四:volatile不会造成线程阻塞,高并发时性能更高,synchronized会造成线程阻塞,高并发效率低

    第五:volatile不能保证操作的原子性,因此它不能保证线程的安全,synchronized能保证操作的原子性,保证线程的安全

    10一个类中两个方法中都有Synchronized(this) 请问能锁住吗?为什么

    都有可能,

    当创建了两个对象进行调用时,就锁不住。因为这时候this指代的是两个不同的对象,并不是同一个锁。

    当创建一个对象进行调用时,就锁得住。因为这时候this指代的是当前唯一的对象,是同一把锁。

  • 相关阅读:
    ​力扣解法汇总790. 多米诺和托米诺平铺
    Synopsys EDA Tools 安装问题记录
    Qt翻译(本地化)坑总结
    详解 Apache Hudi Schema Evolution(模式演进)
    mobaxterm使用
    记录:COMSOL仿真——光子晶体光纤
    关于RabbitMQ的一些面试题
    Kube-OVN-安装配置参数选项
    【Oracle训练营】属于你的9天Oracle实战训练营!
    Qt post 传base64图片 服务器接收解析图片失败
  • 原文地址:https://blog.csdn.net/m0_64210833/article/details/126234024