ReentrantLock是可重入锁,它实现了Lock接口。
可重入锁就是说同一个线程可以多次申请到该锁。
ReentrantLock有公平锁和非公平锁两种方式。
1 | public void m() { |
使用lock和unlock进行加锁和解锁。
而lock和unlock都是调用sync的。
1 | //加锁 |
sync是ReentranrtLock的成员变量,他继承了AQS,所以,核心是AQS的实现,而且有两个内部类,一个可以实现公平锁,一个实现非公平锁。
1 | private final Sync sync; |
在构造函数中可以定义是公平锁还是非公平锁。
1 | public ReentrantLock(boolean fair) { |
ReentrantLock主要还是基于AQS实现的,我们主要关注他重写的一些方法,包括tryAcquire()和release().
tryAcquire()
1 | //非公平锁 |
公平锁和非公平锁最大的区别就是!hasQueuedPredecessors(),公平锁需要先判断等待队列中是否有前驱节点在等待。如果有,则说明有线程比当前线程更早的请求资源,根据公平性,当前线程请求资源失败;如果当前节点没有前驱节点,才有做后面的逻辑判断的必要性。
公平锁
非公平锁的吞吐量较高例如默认状态的ReentrantLock 有新线程来了先争夺一下锁,没成功再去排队。
公平锁是java关键字synchronized的重锁模式,谁来了都乖乖排队,后来的线程不能争夺锁,一定要入队列等待前一个线程来unpark自己,除非队列里没有其他线程。
可以在构造函数中设置公平锁还是非公平锁。
尝试锁定
可以使用tryLock()来尝试上锁,假如在一定的时间内获取锁失败,那么就会放弃等待。
可中断锁
lock.lockInterruptibly() 对线程中断 interrupt() 做出响应。
使用 lockInterruptibly() 则该线程在等待锁的过程中,如果被中断interrupt(),则直接抛出中断异常来立即响应中断。
1 | package JUC; |
与synchronized区别
- ReentrantLock是JDK实现的,synchronized是JVM实现的。
- ReentrantLock需要手动释放,而且最好在finally中释放。
- ReentrantLock支持公平锁。
- ReentrantLock支持中断锁。
- ReentrantLock支持多个条件队列。
- 一个底层实现是CAS,一个实现是操作系统的monitor。
现在来说,经过JVM的优化,synchronized的效率已经很高了,一般来说,如果没有必须要使用ReentrantLock的功能。最好使用synchronized。因为JVM 原生地支持它,而 ReentrantLock 不是所有的 JDK 版本都支持。