ReentrantLock默认是用非公平锁,具体文章见2.ReentrantLock-独占锁
那么公平锁是怎么实现的,它跟非公平锁到底有什么区别。
其实公平锁和非公平锁的实现几乎一样。只有在加锁有一出不同。公平锁会多出一个hasQueuedPredecessors()
方法。如下图,左边是公平锁获取方式,右边是非公平锁获取方式。
1
2
3
4
5
6
7
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
首先去判断有没有其他线程比当前线程等待的事件更长。
- head和tail不相等说明有等待的线程。
- head.next为空 说明可能有线程在入队(在2.ReentrantLock-独占锁中有说明,是由于有节点要挂在链表上的操作不具备原子性导致的。)
- 当前线程不等于head.next节点的线程说明队列里面还是有多个节点
综上,公平锁就是通过同步队列来实现多个线程按照申请锁的顺序(FIFO原则)来获取锁,从而实现公平的特性。非公平锁加锁时不考虑排队等待问题,直接尝试获取锁,所以存在后申请却先获得锁的情况。
在非公平锁中,由于刚释放锁的线程再次获取同步状态的几率会非常大,所以使得其他线程只能在同步队列中等待。公平性锁保证了锁的获取按照FIFO原则,而代价是进行大量的线程切换。非公平性锁虽然可能造成线程“饥饿”,但极少的线程切换,保证了其更大的吞吐量。