概念
重入锁 ReentrantLock,是指支持重进入的锁,表示锁可以支持一个线程对资源的重复加锁,即「任意线程在获取到这个锁之后,如果再次获取该锁,不会被锁阻塞」。重入锁还支持锁时的公平和非公平性(默认)的选择。
实现
实现重入的机制,需要解决以下两个问题:
- 线程需要再次获取锁:锁一定要能够识别获取锁的线程是否是当前占据锁的线程,如果符合,就能够获取成功
- 锁需要得到最终的释放:线程重复了 n 次获取了锁之后,就需要在第 n 次释放锁后,其它的线程才能获取到该锁。主要是通过计数器来实现。锁每获取一次,计数器就要自增 1;每释放一次,计数器就要自减 1,一直减到 0 为止,表示当前线程已经成功释放了该锁,其它线程可以来获取该锁。
重入性和公平性
ReentrantLock 不支持隐式的重入锁,但是可以在调用lock()
方法时,已经获取到锁的线程,能够再次调用lock()
方法获取锁且不被阻塞。ReentrantLock 的公平与否,是通过构造方法来决定的,内部类Sync
继承了AQS
,分为公平锁FairSync和非公平锁NonfairSync。
公平锁和非公平锁
先对锁进行获取的请求肯定是优先执行的,锁获取的顺序也符合请求的绝对时间顺序,类似于 FIFO,那么就是公平锁。反之,就是非公平锁。
以下是公平锁与非公平锁的测试输出结果:
1 | // 公平锁的结果 |
从结果中我们可以看出,公平锁每次都是从同步队列中的第一个节点获取锁,而非公平锁则是会连续两次获取锁。
从开销上来看,公平锁的开销会更大一些,因为它每次都要切换到另一个线程,而对于非公平锁,会出现连续获取锁的对象,切换次数要少一些,所以非公平锁的开销会更小一些。所以,公平锁保证了锁的获取按照顺序进行,保证了公平性,解决了饥饿问题,但是增加了大量的线程切换。