ReentrantLock重入锁,是指对于同一个线程,在再次获取锁时,不会阻塞,是一种优化锁。Synchronized关键字是支持隐式重入的同步,对于同一个线程,可以支持多次进入而不阻塞。
ReentrantLock支持可重入的实现主要是通过AQS中的volatile共享变量state的计数来实现的,对于同一个线程,只会进行计数,不会阻塞,相反,在释放同步状态时,需要释放所有计数,才会释放锁。 ReentrantLock支持公平锁和非公会锁,公平锁是指严格按照FIFO的规则,先阻塞的线程先获取锁,后阻塞的线程后获取锁。ReentrantLock通过内部实现的FairSycn和NonFairSync两个内部类的tryAcquire方法实现的不同来实现公平锁和非公会锁。
首先看ReentrantLock对AbstractQueuedSynchronizer的实现:
1 | static abstract class Sync extends AbstractQueuedSynchronizer { |
非公平锁版本的同步器实现:
1 | final static class NonfairSync extends Sync { |
公平锁的同步器实现:
1 | final static class FairSync extends Sync { |
可以通过ReentrantLock的构造方法来决定是使用公会锁还是非公平锁:
1 | public ReentrantLock(boolean fair) { |
如果使用没有参数的构造方法,使用的是非公平锁版本。
下面的例子测试公平锁和非公会锁的区别
1 | public class FairAndNonFairLockTest { |
程序输出为:
1 | Locked by [Thread-0];waiting by [Thread[Thread-2,5,main],Thread[Thread-1,5,main],Thread[Thread-3,5,main],Thread[Thread-4,5,main]] |
可以看到,是按照等待队列中的线程顺序获取锁的
改为测试非公平锁的情况,输出为:
1 | Locked by [Thread-3];waiting by [Thread[Thread-4,5,main],Thread[Thread-0,5,main],Thread[Thread-1,5,main],Thread[Thread-2,5,main]] |
可以看到,有些线程可以重复获取锁而有些线程则处于“饥饿”的状态。
一般来说,非公平锁比公平锁性能更好,因为可以减少线程大量地切换。