ReentrantLock面试总结

1. 你了解ReentrantLock吗?

  1. ReentrantLock是基于AQS框架实现的锁,它类似于Synchronized互斥锁,可以保证线程安全。
  2. ReentrantLock相比Synchronized,拥有更多的特性,比如支持手动加锁、解锁,支持公平锁等。

2. 什么是AQS?

AQS定义了一套多线程访问共享资源的同步器框架,是一个依赖状态的同步器。AQS定义了很多并发中的行为,比如:
阻塞等待队列、共享/独占、公平/非公平、可重入、允许中断

3. Synchronized和ReentrantLock的相同点?

  1. .ReentrantLock和synchronized都是独占锁,只允许线程互斥的访问临界资源
  2. ReentrantLock和synchronized都是可重入的。synchronized因此可以放在被递归执行的方法上,且不用担心线程最后能否正确释放锁;而ReentrantLock在重入时要却确保重复获取锁的次数必须和重复释放锁的次数一样,否则可能导致其他线程无法获得该锁。

4. Synchronized和ReentrantLock的不同点?

  1. ReentrantLock是Java层面的实现,synchronized是JVM层面的实现
  2. ReentrantLock可以实现公平和非公平锁
  3. ReentantLock获取锁时,限时等待,配合重试机制更好的解决死锁
  4. ReentrantLock 可响应中断
  5. 使用synchronized结合Object上的wait和notify方法可以实现线程间的等待通知机制。ReentrantLock结合Condition接口同样可以实现这个功能。而且相比前者使用起来更清晰也更简单。

5. ReentrantLock是如何实现公平锁和非公平锁呢?

ReentrantLock 默认是不公平的

1. 公平锁

  1. 在获锁之前,通过 !hasQueuedPredecessors() 先看下是否有人排队
  2. 如果没有排队则尝试获锁;如果有排队,则进入排队队列。
  3. 公平锁是先等待的,先获得锁,后来的后获得锁。这是hasQueuedPredecessors()方法的逻辑

2. 非公平锁

非公平锁的情况下,相对较晚来的线程,在尝试上锁的时候,即使之前已经有等待锁的线程存在,它也是有可能上锁成功

6. ReentrantLock如何获得锁呢” />1. 先来看下AbstractQueuedSynchronizer类的结构

  • state同步器状态变量,值为0时表示当前可以被加锁。值为1 时表示有线程占⽤,其他线程需要进⼊到同步队列等待,同步队列是⼀个双向链表。

  • exclusiveOwnerThread:当前获取锁的线程

  • head:指向基于Node类构造的队列的队头,同步队列是⼀个双向链表。

  • tail:指向基于Node类构造的队列的队尾,同步队列是⼀个双向链表。

  • Thread:表示当前线程的引用,比如需要唤醒的线程。

2. 上锁逻辑

  1. 在acquire方法中,如果不进行tryAcquire或者没有加锁成功,那么就会addWaiter到队列

  2. 以公平锁上锁为例,当使用lock()上锁,会传入1作为cas对state状态量的预计值进行修改,前提是查看同步队列中是否没有其他线程等待

  3. 如果没有加锁成功,则尝试进入队列

  4. 虽然说当前线程已经入队列了,但线程还没有阻塞,接下来线程要做阻塞
    阻塞之前,再抢一次锁,如果锁成功,头节点出队列
    若抢锁失败,进行阻塞

  5. 什么时候被唤醒呢?在lock.unlock()中唤醒
    首先
    释放锁
    ,AQS的所属线程置空

    然后唤醒线程