ReentrantLock实现公平锁和非公平锁

在 Java 里,公平锁和非公平锁是多线程编程中用于同步的两种锁机制,它们的主要差异在于获取锁的顺序规则。下面是对二者的详细介绍:

公平锁

公平锁遵循 “先来先服务” 原则,也就是线程获取锁的顺序和请求锁的顺序一致。先请求锁的线程会优先获得锁,这样可以保证每个线程都有公平的机会获取锁,避免某个线程长时间等待。

不过,公平锁在实现公平性时会增加额外的开销,因为需要维护一个有序的等待队列。当一个线程释放锁后,会从队列头部选取下一个线程来获取锁。

非公平锁

非公平锁不保证线程获取锁的顺序和请求锁的顺序一致。当锁被释放时,任何等待的线程都有机会获取锁,而不考虑其请求的先后顺序。

非公平锁可能会让某些线程先于等待时间长的线程获取锁,从而产生 “饥饿” 现象,即部分线程长时间得不到锁。但非公平锁的性能通常比公平锁要好,因为它减少了线程上下文切换和等待队列管理的开销。

实现公平锁和非公平锁

在创建ReentrantLock时可以指定true或者false在指定公平或者非公平锁(ReentrantLock和Synchronized关键字默认是非公平锁),像下面这样

// 创建公平锁
Lock fairLock = new ReentrantLock(true);
// 创建非公平锁
Lock unfairLock = new ReentrantLock(false);

如下是测试ReentrantLock实现公平锁和非公平锁的代码

class Worker implements Runnable {
    private final Lock lock;
    private final String name;

    public Worker(Lock lock, String name) {
        this.lock = lock;
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            lock.lock();
            try {
                System.out.println(name + " 获得锁,正在执行任务 " + i);
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
                System.out.println(name + " 释放锁");
            }
        }
    }
}

public class FairAndUnFair {
    public static void main(String[] args) {
        // 创建公平锁
        Lock fairLock = new ReentrantLock(true);
        // 创建非公平锁
        Lock unfairLock = new ReentrantLock(false);

        // 使用公平锁
        System.out.println("使用公平锁:");
        Thread t1 = new Thread(new Worker(fairLock, "线程1"));
        Thread t2 = new Thread(new Worker(fairLock, "线程2"));
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 使用非公平锁
        System.out.println("\n使用非公平锁:");
        t1 = new Thread(new Worker(unfairLock, "线程1"));
        t2 = new Thread(new Worker(unfairLock, "线程2"));
        t1.start();
        t2.start();
    }
}

在这个示例中,ReentrantLock构造函数的参数true表示创建公平锁,false表示创建非公平锁。

适用场景

  • 公平锁:适用于对公平性要求较高的场景,如任务调度系统,需要保证每个任务都能按顺序执行。
  • 非公平锁:适用于对性能要求较高,且对公平性要求较低的场景,如高并发的缓存系统。
### Java ReentrantLock 公平公平实现原理 #### 公平的特性 在Java并发包`java.util.concurrent.locks.ReentrantLock`中,当创建实例时可以指定是否采用公平策略。对于公平而言,在多个线程竞争同一把的情况下,会按照请求的时间顺序来决定哪个线程获得资源[^1]。 ```java // 创建一个公平 ReentrantLock fairLock = new ReentrantLock(true); ``` #### 公平的行为模式 而公平则不会遵循严格的等待队列规则,允许插队行为发生。这意味着即使有其他线程正在排队等候这把,当前尝试获取该的新线程仍有机会立即占有它而无需严格遵照先入先出的原则[^2]。 ```java // 创建一个公平,默认是公平ReentrantLock unfairLock = new ReentrantLock(); ``` #### 内部机制解析 无论是公平还是公平版本的`ReentrantLock`都依赖于AQS(AbstractQueuedSynchronizer),即抽象队列同步器来进行底层控制逻辑的设计。具体来说: - **状态管理**:通过整型变量表示持有者对该拥有的次数; - **FIFO队列维护**:用于记录那些未能成功取得独占权因而被迫进入休眠态直到被唤醒为止的所有候选线程; - 对于公平,每次都会检查队首是否有待处理的任务;而对于公平,则可能直接尝试去抢夺而不必理会已有多少个前驱节点存在[^3]。 #### AQS源码片段展示如何区分两种类型的定操作 下面给出简化版的伪代码用来说明两者之间的差异之处: ```java final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); // 获取当前状态值 if (c == 0) { // 尝试设置新状态并返回结果, 这里体现了公平性可以直接抢占 if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { // 如果已经是拥有者的再次加情况 int nextc = c + acquires; setState(nextc); return true; } return false; } protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (isHeldExclusively()) { // 已经持有的情况下增加计数即可 return true; } if (c == 0 && !hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } return false; } ``` 上述两个方法分别代表了公平公平条件下试图获取的过程。其中`nonfairTryAcquire()`函数总是优先考虑自己能否立刻得到,而不管前面还有没有人排队;相反地,`tryAcquire()`会在调用之前先判断是否存在更早到来的竞争者(`!hasQueuedPredecessors()`)再做决策[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿黄学技术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值