一、ReentrantLock介绍
ReentrantLock是juc.locks包中的一个独占式可重入锁,相比synchronized,它可以创建多个条件等待队列,还支持公平/非公平锁、可中断、超时、轮询等特性。
ReentrantLock实现Lock接口实现了一个锁所需的方法,如lock()、unLock()等,在这些方法中实际上是调用继承了AQS的同步器Sync对象中的方法来实现对锁资源的获取与释放,而内部类Sync有两个子类FairSync和NonfairSync,分别对应公平锁和非公平锁。ReentrantLock默认构造器是构造公平锁
二、ReentrantLock特性详解
1. 多条件队列
Sync对象中开放了创建AQS中条件队列ConditionObject对象的方法,并重写了isHeldExclusively()方法(通知方法signal()要用到),因此可创建条件队列实现通知等待机制。
2. 非公平 & 公平锁
非公平锁NonfairSync
- 新进来的线程会先直接与同步队列中的线程竞争CAS
- 竞争失败则调用acquire -> tryAcquire -> nonfairTryAcquire继续竞争,再失败才会在acquire方法中后续执行加入同步队列
- 若线程是重入这个锁,会记录重入次数,若超过int范围溢出则抛出错误
公平锁FairSync
- 新来的线程若同步队列为空才竞争锁,否则tryAcquire直接返回false然后进入队列排队,实现先来后到公平锁
- 同样也会记录重入次数
3. 可中断
我们知道synchronized在锁竞争时是不可中断的,获取不到锁的线程会一直处于阻塞状态。而ReentrantLock调用lockInterruptibly()获取锁的过程是可以响应中断的,其内部调用的是AQS的acquireInterruptibly()方法,当收到中断信号时会退出阻塞然后抛出InterruptedException异常从而退出锁竞争。
4. 超时
调用tryLock(long timeout, TimeUnit unit)获取锁可实现超时功能,当超过时间还未获取到锁则直接抛出异常退出锁竞争
内部是AQS中调用LockSupport.parkNanos()超时阻塞实现的
5. 轮询
ReentrantLock的轮询特性是指可通过tryLock()方法尝试获取锁,没获取到则不阻塞直接退出,可以过会再来尝试。tryLock()调用Sync中定义的nonfairTryAcquire方法,从前面列出的源码可知没获取到则直接返回false
三、ReentrantLock类和synchronized关键字的区别
ReentrantLock和synchronized都是独占式可重入锁,但是它们有如下区别:
- 锁实现机制:ReentrantLock是一个类,是基于AQS实现的,依赖于JDK的API;synchronized是一个关键字,是直接在JVM层面通过监视器实现的锁机制。
- 条件等待队列:ReentrantLock通过Condition可创建多个条件等待队列;而synchronized依赖的监视器模型中只有一个等待队列。
- 非公平 & 公平锁:ReentrantLock支持公平和非公平锁;synchronized为非公平锁
- 其他特性:ReentrantLock还支持可中断、超时、轮询等特性;synchronized不支持这些
一般来说在不需要用到ReentrantLock特殊特性的时候就用synchronized,因为synchronized显然相对来说使用起来更加简洁高效
参考:https://blog.csdn.net/zhengzhaoyang122/article/details/110847701