公平锁是指多个线程按照申请锁的顺序来获取锁,即线程会直接进入队列中排队,队列中的第一个线程才能获得锁。这种机制确保了线程获取锁的公平性,避免了“饥饿”现象,即等待时间过长的线程无法获取到锁的情况。
默认情况下,ReentrantLock是非公平的,这是因为非公平锁的效率通常要高于公平锁。在没有特殊需求的情况下,一般推荐使用非公平锁,因为它能提供更高的性能。
通过查看ReentrantLock的构造方法,我们会发现有两种
ReentrantLock lock = new ReentrantLock();ReentrantLock lock = new ReentrantLock(true);
默认调用是NonfairSync()方法创建这个锁
公平锁的实现是在tryAcquire中实现的
方法通过Thread.currentThread()
获取当前线程对象,并将其赋值给变量current
。然后,调用getState()
方法获取当前锁的状态值,将其赋值给变量c
。
判断状态值c
是否为0。如果为0,表示锁未被占用,执行了hasQueuedPredecessors()方法
这个方法是用于判断当前线程是否有前驱节点在等待队列中。
方法首先获取尾节点(tail)和头节点(head),然后通过比较这两个节点是否相等来判断队列是否为空。如果头节点不等于尾节点,说明队列中有元素。
接下来,方法获取头节点的下一个节点(s = h.next),并检查该节点是否为null或者该节点的线程不是当前线程(s.thread != Thread.currentThread())。如果满足这些条件之一,说明当前线程有前驱节点在等待队列中,返回true;否则,返回false。
如果返回false就会执行compareAndSetState()方法,将状态值从0修改为acquires
,则表示成功获取锁。之后调用setExclusiveOwnerThread(current)
方法将当前线程设置为独占锁的拥有者。
如果状态值c
不为0,表示锁已被占用。此时,需要进一步判断当前线程是否是锁的拥有者:
- 如果当前线程是锁的拥有者(即
current == getExclusiveOwnerThread()
),则可以将状态值增加acquires
,并更新锁的状态值。 - 如果增加后的状态值
nextc
小于0,表示超过了最大锁计数,抛出异常。 - 更新锁的状态值为
nextc
。 - 返回
true
表示成功获取锁。