分布式常见面试题:什么是可重入锁,为什么说可重入锁是个糟糕的设计?

面试题 潘老师 2个月前 (02-09) 87 ℃ (0) 扫码查看

在分布式和多线程编程领域,可重入锁是一个常见却又充满争议的概念。不少程序员在面试时会被面试官问到相关问题,却难以清晰作答。今天,咱们就来深入探讨一下可重入锁,不仅了解它的原理,还要分析为何有人认为它是糟糕的设计,同时说明其实际应用场景。

可重入锁的原理

可重入锁的原理理解起来并不复杂。想象一下,在多线程的程序世界里,一个线程获得了一把锁,之后它调用的其他方法可能也需要访问共享资源,也就意味着这些方法也得加锁。如果这把锁支持可重入,那么这个线程在调用这些方法时,就无需再次加锁,能直接使用这把锁。反之,如果锁不可重入,那就得在新的方法上加新的锁,这就容易出现“锁中锁”的情况,加锁的同时还得等待当前锁释放,一不小心就会陷入死锁困境。这就是可重入锁的设计初衷,它能有效避免死锁,保障多线程程序的顺利运行。

在Java的JVM中,synchronized关键字修饰的锁就是可重入锁。例如下面这段代码:

public class ReentrantLockExample {
    public synchronized void method1() {
        // 执行一些操作
        method2();
    }

    public synchronized void method2() {
        // 执行其他操作
    }
}

在上述代码里,当线程进入method1方法获取到锁后,调用method2方法时无需再次获取锁,这就是可重入锁的体现。

分布式环境下可重入锁的实现——以Redisson为例

在分布式环境中,基于Redis的分布式锁应用广泛,Redisson就是其中典型的代表。从Redisson的源码来看,实现可重入锁有两个关键要点。
一是存储当前线程的ID 。要知道,在分布式场景下,不同节点上的线程ID可能会重复,所以得想办法避免。可以通过将进程ID和线程ID组合起来,以此保证ID的唯一性。这样一来,就能明确知道是哪个线程获取了锁。
二是做好计数工作。在Redis中,可以使用incr函数来实现。每次线程重复加锁,就对计数进行增加操作,将这个计数存储在哈希结构中。相应地,释放锁的时候,就得按照计数进行递减操作。只有当计数减为0时,才真正释放锁。

可重入锁为何被认为是糟糕的设计

尽管可重入锁在原理和实现上有其合理性,但仍有人对它提出质疑。比如,Go语言的设计就没有引入可重入锁,其创始人认为可重入锁破坏了某些程序中的“不变量”。简单来说,程序原本一些稳定的状态或规则,因为可重入锁的存在变得复杂多变。而且,可重入锁增加了代码的复杂度,使得程序逻辑更难理解和维护,这无疑加大了出现bug的风险。

可重入锁的应用场景

在实际开发中,寻找可重入锁合适的应用场景确实有点困难。像常见的秒杀、库存扣减这类场景,通常加一把普通的锁就能解决问题,查询库存、扣减库存等操作在这一把锁的控制下有序进行。还有抢优惠券的场景,也可以通过在特定的逻辑中加一把锁来处理,似乎并不需要可重入锁的“特殊能力”。不过,这并不意味着可重入锁毫无用武之地,只是可能在我们日常接触的业务场景中不那么常见。或许在一些复杂的、多层嵌套调用且涉及共享资源频繁竞争的系统中,可重入锁能发挥它的优势。

可重入锁是一个在理论和实践中都充满讨论点的概念。它的原理简单却又有着复杂的实现和应用争议。希望通过这篇文章,大家对可重入锁有更全面的认识。要是你在实际开发中遇到过可重入锁的应用场景,欢迎在评论区分享,咱们一起交流学习!


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/ms/14095.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】