
大厂经常问到公平锁ReentrantLock的实现等,下面我就重点来详解公平锁的使用与实现@mikechen
什么是公平锁
公平锁是一种同步机制,它可以确保在多个线程或进程请求锁的情况下,锁的获取顺序是按照请求顺序来进行的。

公平锁的主要作用:是确保多个线程或进程获取锁的顺序与它们请求锁的顺序一致。
就是很公平,在并发环境中,每个线程在获取锁时会先查看此锁维护的等待队列。
公平锁实现原理
ReentrantLock 的核心实现:依赖于 AQS(AbstractQueuedSynchronizer)。

ReentrantLock 内部使用 Sync 抽象类继承了 AQS,有两个具体子类:
| 类名 | 描述 |
|---|---|
NonfairSync |
非公平锁实现类 |
FairSync |
公平锁实现类 |
当你用 new ReentrantLock(true) 构造时,底层就用的是 FairSync。
如下所示:
ReentrantLock lock = new ReentrantLock(true);
可以将其参数fair设置为true来创建一个公平锁。
在公平锁中,lock()方法会尝试获取锁,并且如果锁当前被其他线程持有,则当前线程会被阻塞,直到锁被释放。
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState(); // 获取当前锁状态
if (c == 0) {
// 公平性判断:是否有线程排在前面
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) {
// 可重入
int nextc = c + acquires;
setState(nextc);
return true;
}
return false;
}
hasQueuedPredecessors() 实现公平性,确保排队,保证“谁先来谁先获得锁”,但性能相对非公平锁略低。
以下是一个简单的示例,展示了公平锁的使用方式:
import java.util.concurrent.locks.ReentrantLock;
public class FairLockExample {
private final ReentrantLock lock = new ReentrantLock(true); // 创建一个公平锁
private int count = 0;
public void increment() {
lock.lock(); // 获取锁
try {
count++; // 进行共享资源的操作
} finally {
lock.unlock(); // 释放锁
}
}
public int getCount() {
lock.lock(); // 获取锁
try {
return count; // 返回共享资源的状态
} finally {
lock.unlock(); // 释放锁
}
}
}
公平锁应用场景
公平锁适用于多个线程或进程共享同一个资源的情况,特别是当这些线程或进程的优先级或重要性相同时。

以下是一些常见的公平锁的应用场景:
1.数据库访问
如果多个线程或进程需要访问同一个数据库,那么使用公平锁可以确保每个线程或进程都有机会获取数据库的访问权限。
2.缓存访问
如果多个线程或进程需要同时访问同一个缓存,那么使用公平锁可以确保每个线程或进程都有机会获取缓存的访问权限。
3.任务调度
在任务调度程序中,如果多个任务需要同时执行,那么使用公平锁可以确保每个任务都有机会被调度执行。
4.消息队列
在消息队列中,如果多个线程或进程需要同时消费同一个消息队列,那么使用公平锁可以确保每个线程或进程都有机会获取消息队列的访问权限。
以上就是公平锁的详解,更多锁,请查看:最全Java锁详解:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁
mikechen睿哥
10年+一线大厂架构实战经验,就职于阿里、淘宝等一线大厂,操盘多个亿级大厂核心项目。