Java中的公平锁和非公平锁
公平锁
特点
-
排队机制:
-
线程请求锁时,如果锁被占用,会进入一个等待队列。
-
当锁被释放时,队列中等待最久的线程优先获得锁。
-
-
避免线程饥饿:
-
公平锁可以防止某些线程长时间无法获取锁的问题。
-
-
性能较低:
-
排队和唤醒线程的开销较高,可能导致整体性能下降。
-
实现方式
在 Java 的 ReentrantLock 中,通过将构造方法的参数 fair 设置为 true 来启用公平锁:
ReentrantLock lock = new ReentrantLock(true);
示例
import java.util.concurrent.locks.ReentrantLock;
public class FairLockExample {
private static final ReentrantLock lock = new ReentrantLock(true);
public static void main(String[] args) {
Runnable task = () -> {
for (int i = 0; i < 2; i++) {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " got the lock");
} finally {
lock.unlock();
}
}
};
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();
}
}
运行结果通常会按照线程的启动顺序依次获取锁,例如:
Thread-1 got the lock
Thread-2 got the lock
Thread-3 got the lock
...
非公平锁
非公平锁(Non-Fair Lock)是默认的锁实现,线程尝试直接竞争锁,不关注排队顺序。
特点
-
竞争机制:
-
每个线程都可以直接尝试获取锁,成功则进入临界区,失败则进入等待队列。
-
-
性能较高:
-
由于不需要维护严格的排队顺序,线程调度和唤醒的开销较小,吞吐量更高。
-
-
可能导致线程饥饿:
-
某些线程可能长时间无法获取锁,因为新来的线程可能会插队成功。
-
实现方式
在 Java 的 ReentrantLock 中,通过将构造方法的参数 fair 设置为 false 或默认值启用非公平锁:
ReentrantLock lock = new ReentrantLock(false);
示例
import java.util.concurrent.locks.ReentrantLock;
public class NonFairLockExample {
private static final ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Runnable task = () -> {
for (int i = 0; i < 2; i++) {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " got the lock");
} finally {
lock.unlock();
}
}
};
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();
}
}
运行结果可能会出现线程抢占的情况,例如:
Thread-1 got the lock
Thread-3 got the lock
Thread-2 got the lock
Thread-3 got the lock
...
公平锁 vs 非公平锁
| 特性 | 公平锁 | 非公平锁 |
|---|---|---|
| 锁分配顺序 | 按线程请求锁的顺序分配 | 随机分配,允许插队 |
| 线程饥饿 | 不会发生 | 可能发生 |
| 性能 | 性能较低,开销较大 | 性能较高,吞吐量大 |
| 使用场景 | 需要严格控制线程公平性 | 更关注性能,允许一定的不公平性 |
选择建议
-
使用公平锁的场景:
-
需要避免线程饥饿,例如在多线程的资源分配中需要确保所有线程都能公平参与竞争。
-
-
使用非公平锁的场景:
-
更注重性能,线程竞争较少时(如大部分操作锁很快释放),非公平锁的性能优势更明显。
-
默认情况下,ReentrantLock

浙公网安备 33010602011771号