ReentrantLock
一.重入锁的特点
1)实现重进入功能
重进入是指任意线程获取锁之后能够再次获取该锁而不会被锁阻塞
锁的获取和释放过程如下:
- 线程再次获取锁。锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是则再次获取成功。
- 锁的最终释放。线程重复n次获取了锁,随后在第n次释放该锁后,其他线程能够获取到该锁。锁的最终释放要求锁对获取进行计数自增,计数表示当前锁被重复获取的次数,而锁被释放时,计数自减,当计数器等于0时表示锁已经成功释放了。
2)分为公平锁和非公平锁:
公平锁:
- 对先发起请求的线程即等待最久的线程优先满足,获取锁是顺序的,符合FIFO原则,不会产生线程饥饿;
- 获取锁调用tryAcquire方法,与非公平锁不一样的地方在于判断条件多了hasQueuedPredecessors()方法,这个方法判断队列中是否有其他节点,如果队列中还有其他节点,但是head后面还没关联节点 / 或者队列中head节点的后继节点关联的线程不是当前线程,如果是返回true,则表示有线程比当前线程更早地请求获取锁,因为要等待前驱节点获取并释放锁后才嫩继续获取到锁。
非公平锁(默认的):
- 获取是使用nonfairTryAcquire方法,只要CAS设置同步状态成功,则当前线程获取了锁。
- 非公平锁比公平锁效率更高,因为公平锁为了保证公平性会去切换线程导致上下文切换,存在额外的开销,所以非公平锁性能更好(所以作为默认的实现方式),保证了更大的吞吐量,但是可能会产生线程饥饿。
二.ReentrantLock和AQS的关系
锁底层大多方法是使用AQS来实现的。
一般我们使用ReentrantLock都是直接创建一个对象,例如下面代码
Lock lock = new ReentrantLock();
下面我们看下ReentrantLock的构造函数
public ReentrantLock() {
//默认实现是以非公平锁实现的
sync = new NonfairSync();
}
从上面这个代码可以看出,我们要分析两个东西:一个是sync,一个是NofairSync(非公平锁):
//ReentrantLock实现了Lock
public class ReentrantLock implements Lock, java.io.Serializable {
//我们刚刚要找的sync字段
private final Sync sync;
//Sync继承了AbstractQueuedSynchronizer
abstract static class Sync extends AbstractQueuedSynchronizer {
1.sync分析:从上面源码可以看出sync是ReentrantLock内的属性,而且Sync是ReentrantLock的内部类,并且继承了AbstractQueuedSynchronizer,这个就是我们常常说的AQS,再进入AQS类看下:
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
从上面源码可以看出AbstractQueuedSynchronizer继承AbstractOwnableSynchronizer,也就是AQS继承AOS(后面都用AQS代表AbstractQueuedSynchronizer,AOS代表AbstractOwnableSynchronizer),我们再看看还没分析的NofairSync;
public class ReentrantLock implements Lock, java.io.Serializable {
//NonfairSync继承Sync
static final class NonfairSync extends Sync {
2.NofairSync分析:从上面源码可以看出NonfairSync也是ReentrantLock的内部类,并且继承Sync,难怪刚刚new NonfairSync()可以直接赋值给sync我们再看下ReentrantLock类的结构:
到这里我们可以总结下:
1.ReentrantLock下面有三个内部类:Sync,NonfairSync,FairSync
2.AQS继承AOS
2.Sync继承AQS
3.NonfairSync(非公平锁)、FairSync(公平锁)分别继承Sync
那我们可以得出UML图
AQS和ReentrantLock参考源码分析:
https://guguoyu.blog.csdn.net/article/details/90485326