谈你对公平锁和非公平锁的理解
在Java中,公平锁(Fair Lock)和非公平锁(Non-fair Lock)是两种不同的锁获取策略,它们主要体现在锁的获取顺序上。下面我将分别解释这两种锁的概念,并提供代码示例和分析比较。
公平锁(Fair Lock)
公平锁是指多个线程按照申请锁的顺序去获取锁。如果一个线程请求一个公平锁,它将会在其他线程释放锁之后按照请求的顺序获得该锁。公平锁可以避免线程饥饿,提高系统的公平性,但可能降低吞吐量。
代码示例:
public class FairLockExample {
private final ReentrantLock lock = new ReentrantLock(true);
public void doSomething() {
lock.lock();
try {
// 临界区代码
} finally {
lock.unlock();
}
}
}
在上面的代码中,ReentrantLock 构造函数的第一个参数设置为 true 表示创建一个公平锁。
非公平锁(Non-fair Lock)
非公平锁是指在锁的获取上没有顺序限制,线程获取锁的顺序是不确定的。如果一个线程请求一个非公平锁,它可能会立即获得该锁,即使有其他线程已经等待了更长时间。非公平锁可能提高吞吐量,但可能导致线程饥饿。
代码示例:
public class NonFairLockExample {
private final ReentrantLock lock = new ReentrantLock(false);
public void doSomething() {
lock.lock();
try {
// 临界区代码
} finally {
lock.unlock();
}
}
}
在这个例子中,ReentrantLock 构造函数的第一个参数设置为 false 表示创建一个非公平锁。
分析比较
1. 公平性:
- 公平锁:保证了线程获取锁的顺序,避免了线程饥饿。
- 非公平锁:获取锁的顺序不确定,可能会导致线程饥饿。
2. 吞吐量:
- 公平锁:可能降低系统的吞吐量,因为需要维护一个等待队列。
- 非公平锁:可能提高系统的吞吐量,因为减少了锁的等待时间。
3. 性能:
- 公平锁:由于需要维护等待队列,可能会增加一些性能开销。
- 非公平锁:通常性能更好,因为减少了锁的等待和上下文切换。
4. 使用场景:
- 公平锁:适用于需要保证任务顺序执行的场景,如任务调度。
- 非公平锁:适用于对性能要求较高,可以容忍一定程度线程饥饿的场景。
公平锁和非公平锁的实现原理
公平锁和非公平锁的实现原理主要依赖于锁的内部状态和线程的调度机制。下面我将详细解释它们的实现原理和步骤。
公平锁的实现原理和步骤
-
等待队列:公平锁通常使用一个等待队列来维护线程请求锁的顺序。当线程请求锁时,如果锁已被占用,它会被添加到队列的末尾。
-
锁状态检查:线程尝试获取锁时,首先检查锁的状态。如果锁是空闲的,线程将获得锁并继续执行。
-
队列中的线程调度:如果锁被占用,线程将被添加到等待队列中,并进入等待状态。锁的持有者释放锁时,会检查等待队列,如果有线程在等待,将按照队列中的顺序唤醒等待的线程。
-
线程唤醒:当锁被释放时,队列中的第一个线程将被唤醒并尝试获取锁。这保证了线程按照请求的顺序获得锁。
-
锁的重入:公平锁也支持锁的重入,即同一个线程可以多次获取同一个锁。
非公平锁的实现原理和步骤
-
无等待队列:非公平锁通常不使用等待队列来维护线程请求锁的顺序。线程尝试获取锁时,如果锁已被占用,线程可能会立即尝试再次获取锁,而不是进入等待状态。
-
尝试获取锁:线程尝试获取锁时,会直接尝试获取锁。如果锁是空闲的,线程将获得锁并继续执行。
-
自旋等待:如果锁被占用,线程可能会进行自旋等待,即在循环中不断尝试获取锁,直到成功或达到某个条件后放弃。
-
锁的重入:非公平锁同样支持锁的重入。
-
线程调度:在非公平锁中,线程调度的顺序是不确定的。即使有线程已经在等待锁,新来的线程也可能立即获得锁。
最后
在实际应用中,选择公平锁还是非公平锁需要根据具体场景和需求来决定。如果对公平性有较高要求,可以选择公平锁;如果对性能有较高要求,可以选择非公平锁。公平锁和非公平锁的实现通常依赖于底层的同步机制和线程调度算法。
转载自:https://juejin.cn/post/7386587421423665202