ReentrantLock面试总结
1. 你了解ReentrantLock吗?
- ReentrantLock是基于AQS框架实现的锁,它类似于Synchronized互斥锁,可以保证线程安全。
- ReentrantLock相比Synchronized,拥有更多的特性,比如支持手动加锁、解锁,支持公平锁等。
2. 什么是AQS?
AQS定义了一套多线程访问共享资源的同步器框架,是一个依赖状态的同步器。AQS定义了很多并发中的行为,比如:
阻塞等待队列、共享/独占、公平/非公平、可重入、允许中断
3. Synchronized和ReentrantLock的相同点?
- .ReentrantLock和synchronized都是独占锁,只允许线程互斥的访问临界资源
- ReentrantLock和synchronized都是可重入的。synchronized因此可以放在被递归执行的方法上,且不用担心线程最后能否正确释放锁;而ReentrantLock在重入时要却确保重复获取锁的次数必须和重复释放锁的次数一样,否则可能导致其他线程无法获得该锁。
4. Synchronized和ReentrantLock的不同点?
- ReentrantLock是Java层面的实现,synchronized是JVM层面的实现
- ReentrantLock可以实现公平和非公平锁
- ReentantLock获取锁时,限时等待,配合重试机制更好的解决死锁
- ReentrantLock 可响应中断
- 使用synchronized结合Object上的wait和notify方法可以实现线程间的等待通知机制。ReentrantLock结合Condition接口同样可以实现这个功能。而且相比前者使用起来更清晰也更简单。
5. ReentrantLock是如何实现公平锁和非公平锁呢?
ReentrantLock 默认是不公平的
1. 公平锁
- 在获锁之前,通过 !hasQueuedPredecessors() 先看下是否有人排队
- 如果没有排队则尝试获锁;如果有排队,则进入排队队列。
- 公平锁是先等待的,先获得锁,后来的后获得锁。这是hasQueuedPredecessors()方法的逻辑
2. 非公平锁
非公平锁的情况下,相对较晚来的线程,在尝试上锁的时候,即使之前已经有等待锁的线程存在,它也是有可能上锁成功的
6. ReentrantLock如何获得锁呢” />1. 先来看下AbstractQueuedSynchronizer类的结构
state:同步器状态变量,值为0时表示当前可以被加锁。值为1 时表示有线程占⽤,其他线程需要进⼊到同步队列等待,同步队列是⼀个双向链表。
exclusiveOwnerThread:当前获取锁的线程
head:指向基于Node类构造的队列的队头,同步队列是⼀个双向链表。
tail:指向基于Node类构造的队列的队尾,同步队列是⼀个双向链表。
Thread:表示当前线程的引用,比如需要唤醒的线程。
2. 上锁逻辑
在acquire方法中,如果不进行tryAcquire或者没有加锁成功,那么就会addWaiter到队列
以公平锁上锁为例,当使用lock()上锁,会传入1作为cas对state状态量的预计值进行修改,前提是查看同步队列中是否没有其他线程等待。
如果没有加锁成功,则尝试进入队列
虽然说当前线程已经入队列了,但线程还没有阻塞,接下来线程要做阻塞
阻塞之前,再抢一次锁,如果锁成功,头节点出队列
若抢锁失败,进行阻塞
什么时候被唤醒呢?在lock.unlock()中唤醒
首先释放锁,AQS的所属线程置空
然后唤醒线程