• 面试:ReentrantLock对比Synchronized


    Lock获取锁的方法:

    lock()、tryLock()、tryLock(long time, TimeUnit unit) 和 lockInterruptibly()都是用来获取锁的。

    (1) lock()方法是平常使用得最多的一个方法,就是用来获取锁。如果锁已被其他线程获取,则进行等待。

    (2) tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。

    (3) tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。

    (4) lockInterruptibly()方法比较特殊,当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就使说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。

    ReentrantLock对比Synchronized

    1:synchronized无法判断,是否已经获取到锁,而Lock通过tryLock()方法可以判断,是否已获取到锁;

    2:Lock可以通过分别定义读写锁提高多个线程读操作的效率。

    3:二者的底层实现不一样:synchronized是同步阻塞,采用的是悲观并发策略;Lock是同步非阻塞,采用的是乐观并发策略(底层基于CAS+AQS实现)

    1、死锁预防:这个是比较容易忽视的一点

    Lock可以选择性的获取锁,如果一段时间获取不到,可以放弃。

    synchronized不行,会一根筋一直获取下去。

    借助Lock的这个特性,就能够规避死锁,synchronized必须通过谨慎和良好的设计,才能减少死锁的发生。

    2、Lock是一个接口,Lock是代码层面的实现;

    而synchronized是Java中的关键字,synchronized是内置的语言实现

    3、synchronized在发生异常和同步块结束的时候,会自动释放锁;

    而Lock必须手动释放, 所以如果忘记了释放锁,一样会造成死锁

    4、synchronized底层采用的是objectMonitor;

    lock采用的CAS+AQS

    5、synchronized是阻塞式加锁;

    lock是非阻塞式加锁支持可中断式加锁,支持超时时间的加锁;

    6、synchronized在进行加锁解锁时,只有一个同步队列和一个等待队列;

     lock有一个同步队列,可以有多个等待队列;

    7、synchronized只支持非公平锁;lock支持非公平锁和公平锁;

    8、synchronized使用了object类的wait和notify进行等待和唤醒;

     lock使用了condition接口进行等待和唤醒(await和signal);

    9、与synchronized 不同的是,一旦synchronized 块结束,就会自动释放对someObject的占用。 lock却必须调用unlock方法进行手动释放,为了保证释放的执行,往往会把unlock() 放在finally中进行;

    10、trylock会在指定时间范围内试图占用。 如果时间到了,还占用不成功,就选择放弃;

    注意: 因为使用trylock有可能成功,有可能失败,所以后面unlock释放锁的时候,需要判断是否占用成功了,如果没占用成功也unlock,就会抛出异常;

    11、使用synchronized方式进行线程交互,用到的是同步对象的wait,notify和notifyAll方法;

    Lock也提供了类似的解决办法,首先通过lock对象得到一个Condition对象,然后分别调用这个Condition对象的:await, signal,signalAll 方法;

    注意: 不是Condition对象的wait,nofity,notifyAll方法,是await,signal,signalAll;

    ReentrantLock中的AQS分析 

    java并发AQS原理_沙漠一只雕得儿得儿的博客-CSDN博客_aqs java

    ReentrantLock实现原理及源码分析 - dreamcatcher-cx - 博客园

    深入理解ReentrantLock的实现原理 - 掘金

    ReentrantLock的核心,是通过修改AQS中state的值来同步锁的状态,实现了可重入。

    ReentrantLock具备公平锁和非公平锁,默认使用非公平锁。实现原理依赖于AQS中的同步队列。

    ReentrantLock是一种可重入的,可实现公平性的互斥锁,它的设计基于AQS框架,可重入和公平性的实现逻辑都不难理解,每重入一次,state就加1,当然在释放的时候,也得一层一层释放。至于公平性,在尝试获取锁的时候多了一个判断:是否有比自己申请早的线程在同步队列中等待,若有,去等待;若没有,才允许去抢占。  

    可重入原理:

    ReentrantLock的可重入功能基于AQS的同步状态:state。

    其原理大致为:当某一线程获取锁后,将state值+1,并记录下当前持有锁的线程,再有线程来获取锁时,判断这个线程与持有锁的线程是否是同一个线程,如果是,将state值再+1,如果不是,阻塞线程。 当线程释放锁时,将state值-1,当state值减为0时,表示当前线程彻底释放了锁,然后将记录当前持有锁的线程的那个字段设置为null,并唤醒其他线程,使其重新竞争锁。

    非公平锁示例图:

    公平锁示例图:

    加锁过程:

    1.先获取state值,若为0,意味着此时没有线程获取到资源,CAS将其设置为1,设置成功则代表获取到排他锁了;

    2.若state大于0,肯定有线程已经抢占到资源了,此时再去判断是否就是自己抢占的,是的话,state累加,返回true,重入成功,state的值即是线程重入的次数;

    3.其他情况,则获取锁失败。

     解锁过程:

  • 相关阅读:
    “之江创客”跨境电商赛区决赛暨浙南新电商发展论坛圆满落幕
    Maven聚合项目配合Springcloud案例
    G1D23-RAGA&报名蓝桥&Attackg&安装cuda&torch
    有哪些SQL优化的手段?
    数据结构与算法之美09(排序)
    Stream流源码解析调用过程
    npm常用命令
    抽象类 +接口
    Ansible 自动化运维工具 学习笔记
    Mysql函数
  • 原文地址:https://blog.csdn.net/cpcpcp123/article/details/127706593