MelonBlog

java中锁的公平性是什么

用过ReentrantLock的同学应该知道这个类的构造参数是能够接收一个布尔变量,如果布尔值为true,那么这把锁就是公平锁(FairSync),如果为false,则为非公平锁(NonfairSync),默认情况下是非公平锁。

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

那公平锁和非公平锁所谓的公平性是什么呢?

Fairness

ReentrantLock在内部定义了一个子类Sync,而Sync又继承自AbstractQueuedSynchronizer

AbstractQueuedSynchronizer的文档上写了,它实际上是维护了一个FIFO队列.FairSyncNonfairSync都是继承自Sync。


ReentrantLock中,使用FairSync模式和NonfairSync模式最核心的差异就是怎么去使用这个FIFO队列。


FairSync模式在调用lock() 方法的时候会先去校验这个FIFO队列是否有等待中的线程,如果没有,则允许当前线程获取锁,如果FIFO队列中存在等待中的线程,则需要判断当前线程是否在这个FIFO队列的头部,头部线程代表着它是最早进行等待的线程,也是等待时间最久的线程,所以基于先进线出的原则,必须要让等待时间最久的线程先拿到锁。

NonfairSync模式在调用lock()方法时,会直接判断锁是否被其他线程占用,无论FIFO队列里是否有等待的线程,只要锁被释放了, 当前线程可以跳过等待的线程直接获取锁,如果锁还未被释放,则当前线程还是需要进入FIFO队列。


无论是FairSync模式,还是NonfairSync模式,只要进入了FIFO队列,后续获取锁的流程都是一模一样的, 唯一的区别就是NonfairSync可以再进入FIFO队列之前,提前参与一次锁竞争


那你可能会想到,非公平锁仅仅是多了一次提前获取锁的机会,后续还是该排队获取锁,那为什么后续不随机顺序获取锁呢?

我的理解是,jvm能够使用一套流程就实现两种模式,节省了很多实现的成本

性能差异

在理解FairSync和NonfairSync的公平性差异之后,我们知道非公平锁在流程上有一个优势,就是获取锁的时候有机会直接获取而不用进入等待队列,这样对于jvm来说,减少了一次线程的上下文切换的成本,性能上是要优于公平锁的。所以在业务上没有特殊要求的情况下,我们可以直接使用ReentrantLock默认的NonfairSync模式。