Java公平锁和非公平锁加锁流程
在Java中,ReentrantLock
提供了公平锁和非公平锁两种模式。它们的加锁流程有所不同,主要体现在线程获取锁的顺序上。下面我会用 流程图 和 文字说明 来详细解释这两种锁的加锁流程。
1. 公平锁(Fair Lock)
公平锁的特点是按照线程请求锁的顺序来分配锁,即先到先得。它的加锁流程如下:

流程说明:
- 线程尝试获取锁。
- 如果锁空闲,检查是否有其他线程在等待队列中:
- 如果没有等待队列,当前线程直接获取锁。
- 如果有等待队列,当前线程加入队列尾部,等待被唤醒。
- 如果锁被占用,当前线程加入等待队列尾部,等待被唤醒。
- 当锁释放时,队列中的第一个线程会被唤醒并尝试获取锁。
2. 非公平锁(Non-Fair Lock)
非公平锁的特点是线程可以直接尝试获取锁,而不考虑等待队列的顺序。它的加锁流程如下:

流程说明:
- 线程尝试获取锁。
- 如果锁空闲,直接尝试获取锁:
- 如果获取成功,线程继续执行。
- 如果获取失败(例如被其他线程抢先),线程加入等待队列尾部。
- 如果锁被占用,线程直接加入等待队列尾部,等待被唤醒。
- 当锁释放时,队列中的线程会被唤醒并尝试获取锁,但新来的线程也可以直接尝试获取锁,可能导致“插队”现象。
3. 公平锁 vs 非公平锁的区别
特性 | 公平锁 | 非公平锁 |
---|---|---|
获取锁的顺序 | 严格按照等待队列的顺序(先到先得) | 允许插队,新线程可能直接获取锁 |
性能 | 较低,因为需要维护队列顺序 | 较高,减少了线程切换的开销 |
适用场景 | 需要严格公平性的场景 | 对性能要求较高的场景 |
4. 代码示例
以下是使用 ReentrantLock
的公平锁和非公平锁的示例:
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
public static void main(String[] args) {
// 公平锁
ReentrantLock fairLock = new ReentrantLock(true);
// 非公平锁
ReentrantLock nonFairLock = new ReentrantLock(false);
Runnable task = () -> {
fairLock.lock(); // 或 nonFairLock.lock()
try {
System.out.println(Thread.currentThread().getName() + " 获取锁");
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
fairLock.unlock(); // 或 nonFairLock.unlock()
System.out.println(Thread.currentThread().getName() + " 释放锁");
}
};
Thread t1 = new Thread(task, "Thread-1");
Thread t2 = new Thread(task, "Thread-2");
Thread t3 = new Thread(task, "Thread-3");
t1.start();
t2.start();
t3.start();
}
}
5. 总结
- 公平锁:保证线程获取锁的顺序,但性能较低。
- 非公平锁:允许线程插队,性能较高,但可能导致某些线程长时间等待。
根据具体场景选择合适的锁模式,可以更好地平衡公平性和性能。