问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

深入理解ReentrantLock公平锁:原理、实现与面试要点

创作时间:
作者:
@小白创作中心

深入理解ReentrantLock公平锁:原理、实现与面试要点

引用
CSDN
8
来源
1.
https://blog.csdn.net/qq_43506040/article/details/138031732
2.
https://blog.csdn.net/weixin_53391173/article/details/139231832
3.
https://blog.csdn.net/m0_50672338/article/details/136238770
4.
https://blog.csdn.net/yoonamessir/article/details/138295487
5.
https://blog.csdn.net/m0_74105656/article/details/137366545
6.
https://www.cnblogs.com/hld123/p/18263975
7.
https://www.cnblogs.com/dxflqm/p/18029931
8.
https://www.cnblogs.com/kuangdaoyizhimei/p/18635648

在Java并发编程中,ReentrantLock是一个非常重要的同步工具,它提供了比synchronized更强大和灵活的锁机制。特别是在面试中,ReentrantLock的相关知识是大厂考察的重点,其中公平锁和非公平锁的区别更是必考知识点。本文将深入解析ReentrantLock的公平锁模式,帮助读者更好地理解和掌握这一重要概念。

01

什么是公平锁?

公平锁(Fair Lock)是一种按照线程请求锁的顺序来分配锁的机制。在公平锁模式下,线程按照先来后到的顺序获取锁,严格遵循FIFO(First-In-First-Out)原则。这种机制确保了线程间的公平性,避免了某些线程因等待时间过长而出现"饥饿"现象。

02

公平锁的实现原理

ReentrantLock的公平锁模式是基于AQS(AbstractQueuedSynchronizer)实现的。AQS是一个用于构建锁和同步器的基础框架,它使用了一个双向链表来存储等待锁的线程。当一个线程请求锁时,如果锁已被其他线程持有,该线程就会被封装成一个节点(Node)并添加到链表的尾部,形成一个同步队列。

在公平锁模式下,线程在获取锁时会检查队列中是否有等待的线程。如果有等待线程,新来的线程就会被添加到队列的尾部并进入等待状态。只有当队列中的线程按顺序释放锁后,下一个线程才能获取锁资源。

03

代码示例

下面通过一个简单的代码示例来演示公平锁和非公平锁的区别:

import java.util.concurrent.locks.ReentrantLock;

public class FairLockDemo {
    private static final ReentrantLock fairLock = new ReentrantLock(true);
    private static final ReentrantLock unfairLock = new ReentrantLock(false);

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                for (int j = 0; j < 2; j++) {
                    fairLock.lock();
                    try {
                        System.out.println("公平锁 - 当前线程:" + Thread.currentThread().getName());
                    } finally {
                        fairLock.unlock();
                    }
                }
            }).start();

            new Thread(() -> {
                for (int j = 0; j < 2; j++) {
                    unfairLock.lock();
                    try {
                        System.out.println("非公平锁 - 当前线程:" + Thread.currentThread().getName());
                    } finally {
                        unfairLock.unlock();
                    }
                }
            }).start();
        }
    }
}

运行上述代码,我们可以观察到以下输出:

公平锁 - 当前线程:Thread-0
公平锁 - 当前线程:Thread-1
公平锁 - 当前线程:Thread-2
公平锁 - 当前线程:Thread-0
公平锁 - 当前线程:Thread-1
公平锁 - 当前线程:Thread-2

非公平锁 - 当前线程:Thread-3
非公平锁 - 当前线程:Thread-3
非公平锁 - 当前线程:Thread-4
非公平锁 - 当前线程:Thread-4
非公平锁 - 当前线程:Thread-5
非公平锁 - 当前线程:Thread-5

从输出结果可以看出,公平锁严格按照线程启动的顺序获取锁,而非公平锁则允许线程插队,导致获取锁的顺序可能与启动顺序不一致。

04

性能对比

虽然公平锁保证了线程间的公平性,但这种机制也带来了额外的性能开销。每次线程获取锁时都需要检查队列状态,频繁的上下文切换会导致系统开销增大。相比之下,非公平锁通过减少不必要的上下文切换,提高了系统的整体吞吐量。

根据《Java并发编程实战》中的测试数据,非公平锁的吞吐率(单位时间内成功获取锁的平均速率)通常比公平锁高很多。这是因为非公平锁允许线程在锁释放的瞬间立即获取锁,避免了线程的休眠和唤醒操作。

05

使用场景

选择使用公平锁还是非公平锁,主要取决于具体的应用场景:

  • 公平锁:适用于对公平性要求较高的场景,如任务调度系统、资源分配系统等。在这些场景中,保证每个线程都能及时获取资源是非常重要的。

  • 非公平锁:适用于对性能要求较高的场景,特别是在高并发环境下。虽然可能会出现线程饥饿的情况,但整体系统的处理能力更强。

06

面试要点

在面试中,面试官可能会从以下几个方面考察你对ReentrantLock公平锁的理解:

  1. 公平锁和非公平锁的区别:需要清晰解释两种模式在锁获取策略上的不同。

  2. 实现原理:能够说明公平锁是如何通过AQS实现的,以及这种实现带来的性能影响。

  3. 使用场景:能够根据具体场景选择合适的锁模式,并解释原因。

  4. 性能对比:理解两种模式的性能特点,以及为什么非公平锁通常性能更好。

通过本文的讲解,相信你已经对ReentrantLock的公平锁模式有了深入的理解。在面试中,如果遇到相关问题,可以从锁的获取策略、实现原理、性能特点和使用场景等多个维度进行回答,展现出你对Java并发编程的深刻理解。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号