Java中的公平锁和非公平锁

公平锁和非公平锁是多线程编程中对锁获取策略的两种不同实现,主要区别在于锁对等待线程的调度方式。


公平锁

公平锁(Fair Lock)遵循 先来先服务 的原则,线程按照请求锁的顺序依次获取锁。

特点

  1. 排队机制

    • 线程请求锁时,如果锁被占用,会进入一个等待队列。

    • 当锁被释放时,队列中等待最久的线程优先获得锁。

  2. 避免线程饥饿

    • 公平锁可以防止某些线程长时间无法获取锁的问题。

  3. 性能较低

    • 排队和唤醒线程的开销较高,可能导致整体性能下降。

实现方式

在 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)是默认的锁实现,线程尝试直接竞争锁,不关注排队顺序。

特点

  1. 竞争机制

    • 每个线程都可以直接尝试获取锁,成功则进入临界区,失败则进入等待队列。

  2. 性能较高

    • 由于不需要维护严格的排队顺序,线程调度和唤醒的开销较小,吞吐量更高。

  3. 可能导致线程饥饿

    • 某些线程可能长时间无法获取锁,因为新来的线程可能会插队成功。

实现方式

在 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 非公平锁

特性公平锁非公平锁
锁分配顺序 按线程请求锁的顺序分配 随机分配,允许插队
线程饥饿 不会发生 可能发生
性能 性能较低,开销较大 性能较高,吞吐量大
使用场景 需要严格控制线程公平性 更关注性能,允许一定的不公平性

选择建议

  1. 使用公平锁的场景

    • 需要避免线程饥饿,例如在多线程的资源分配中需要确保所有线程都能公平参与竞争。

  2. 使用非公平锁的场景

    • 更注重性能,线程竞争较少时(如大部分操作锁很快释放),非公平锁的性能优势更明显。

默认情况下,ReentrantLock 使用非公平锁,因为在大多数场景下性能优先于严格的公平性。

posted @ 2024-11-22 17:06  luorx  阅读(380)  评论(0)    收藏  举报