Java面试高频场景题:在工作中或者一些组件源码里面或多或少会用到锁,你能整理概括下锁的分类么?

面试官:在我们的开发过程中,离不开锁,也有很多锁相关的概念,你能梳理下么?

面试者:锁的话有乐观锁悲观锁读写锁,还可以基于Mysql、Redis、ZK去实现分布式锁

面试官:

关于锁的概念有很多,我们可以根据不同的维度去讲。

第一个维度,是从锁的"特性"来分,可以分为乐观锁与悲观锁

  • 悲观锁 认为并发冲突是常态,所以在操作数据前一定要先加锁。它的思想是"先保护,再操作"。synchronizedReentrantLock在独占模式下就是典型的悲观锁实现。
  • 乐观锁 认为并发冲突是偶然的,所以先直接进行操作,在提交更新时再检测冲突。它的思想是"先操作,有冲突再解决"。最常见的实现方式是版本号机制 或**CAS操作**。Java中的原子类(如AtomicInteger)就是基于CAS的乐观锁。

第二个维度,是从锁的"设计"与"竞争策略"来分,可以分为公平锁与非公平锁这关系到锁的性能和公平性。

公平锁​ 遵循先来后到的原则,线程按照申请锁的顺序排队获取。优点是避免了线程饥饿,缺点是整体吞吐量较低,因为要维护一个队列。

非公平锁 ​ 允许"插队",新来的线程有机会直接尝试获取锁,获取不到再排队。优点是减少了线程切换的开销,吞吐率高,但可能导致某些线程长时间等待。ReentrantLock可以指定公平或非公平,因为底层的aqs本来就有维护队列,而synchronized非公平锁

第三个维度,是关于锁的"状态"和"可重复性", 分为可重入锁与不可重入锁

可重入锁 ​ 允许同一个线程在已经持有锁的情况下,再次获取同一把锁。synchronizedReentrantLock都是可重入锁。这可以防止线程自己把自己锁死。

不可重入锁则相反,线程不能重复获取锁,如果使用不当就会造成死锁。所以java里面的锁基本上都是可重入锁

第四个维度,是从"共享性"来分,这决定了锁的粒度。 可以分为独占锁(排他锁/写锁)与共享锁(读锁)

独占锁 ​ 一次只允许一个线程访问资源,如ReentrantLocksynchronized。主要用于写操作。

共享锁 ​ 允许多个线程同时访问资源,如ReadWriteLock中的ReadLock。主要用于读操作。读写锁(ReentrantReadWriteLock)是这两种策略的经典结合,遵循"读读共享,读写互斥,写写互斥"的原则,能极大提升读多写少场景的性能。

除了以上四个主要维度,在实际的Java并发编程和源码中,我们还会接触到一些更具体、更高级的锁概念:

  • 自旋锁 : 当线程尝试获取锁失败时,不会立即挂起,而是循环重试(自旋)。这避免了线程切换的开销,但会消耗CPU。适用于锁被持有时间很短的场景。CAS操作底层就隐含了自旋。

  • 自适应自旋锁: 是自旋锁的优化,自旋的时间不再固定,而是由前一次在同一个锁上的自旋成功与否及持有者状态来决定,更加智能。

  • 锁消除: 是JIT编译器的优化,当编译器检测到某段代码的锁不可能存在共享数据竞争时,就会将这个锁"消除"掉。

  • 锁粗化: 也是编译器的优化。如果虚拟机探测到一连串零碎的操作都对同一个对象反复加锁解锁,会把加锁同步的范围"粗化"到整个操作序列的外部,以减少不必要的性能损耗。

  • 偏向锁/轻量级锁/重量级锁 : 这是synchronized在JVM内部的锁升级机制,是为了在无竞争和低竞争时减少性能开销。

    • 偏向锁: 适用于只有一个线程访问的场景,直接在对象头标记线程ID,几乎无开销。

    • 轻量级锁 : 当有轻微竞争(交替执行)时,通过CAS竞争锁,失败则自旋尝试。

    • 重量级锁: 当竞争激烈(自旋失败),会升级为基于操作系统互斥量(Mutex)的锁,线程会进入阻塞队列,进行用户态到内核态的切换,开销最大。

集群微服务、分布式的区别

面试官:我看你的项目也是用的微服务架构,也做了集群部署,工作中也用到了分布式锁啥的。你能跟我讲下集群、微服务、分布式的区别么?

面试者: 集群的话,就是可以部署主从,微服务的话,就是做服务拆分,做解耦,分布式的话 ,服务拆分应该也属于分布式吧。

面试官: OK,这个看似是概念的问题,但是面试官其实更希望结合你的经验、项目来去回答这个问题,因为这些概念在工作中一定能接触到的。

我跟你讲下我的理解。

集群 ,说白了就是"同一个服务,多部署几份"。当一个服务扛不住压力了,我就把它复制出来,在多台机器上多跑几个实例。然后前面挂个负载均衡,把流量均匀分给这些实例。它的核心就是 "复制"和"分摊" ,专门解决 "量不够" 的问题,提升处理能力,防止一个实例挂了导致全瘫。

微服务 ,思路就不同了,它不再是复制,而是 "拆分" 。它把原来一个庞大复杂的单体应用,按照不同的业务功能,拆分成一堆小型、独立的服务。比如拆成用户服务、订单服务、支付服务。每个小服务都高度自治,可以用自己的技术栈,有自己的数据库,能独立开发、测试、部署和伸缩运维。它们之间通过清晰的 API(比如 REST 或 gRPC)来通信协作。它的核心价值是提升 "敏捷性""可维护性" ,让团队能并行开发,快速迭代,降低系统耦合度。当然,微服务里的每个独立服务,自己也可以用集群的方式来部署,保证它自身的高可用。

分布式 ,这个概念更大,是一个总称。简单理解:只要一个系统的不同组件(或功能)是部署在多台机器/进程上,需要通过网络通信来协同工作,那它就是分布式的 。像分布式锁、事务这些复杂问题,之所以出现,就是因为状态和操作"跨机器"了,不再受单一程序控制。

所以,无论是 集群 (多个相同服务实例协作),还是 微服务 (多个不同服务协作),它们的具体架构模式,都属于 分布式系统 这个大范畴

总结一下关系:

  • 集群 :侧重 横向复制扩展,解决单点瓶颈和可用性。
  • 微服务 :侧重 业务拆分解耦,解决复杂性和开发效率。
  • 分布式 :是涵盖集群、微服务的 总体架构特征
相关推荐
sheji34162 小时前
【开题答辩全过程】以 基于springboot的健身预约系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
文心快码BaiduComate2 小时前
Comate 3月全新升级:全新Plan模式、Explore Subagent深度检索能力增强
前端·后端·程序员
AMoon丶2 小时前
Golang--协程调度
linux·开发语言·后端·golang·go·协程·goroutine
饕餮争锋2 小时前
Supabase使用演示
后端·开源
aZhe的全栈知识分享2 小时前
OpenClaw(龙虾)太难装?这份保姆级教程让你 3 分钟搞定
前端·人工智能·后端
Java编程爱好者2 小时前
Claude Code 最佳实践:可验证、可治理、可分层的工程现实
后端
写Cpp的小黑黑2 小时前
Cursor Chrome DevTools MCP 配置指南 for macOS
后端
神奇小汤圆3 小时前
为什么 synchronized 不能防止指令重排序?
后端
AMoon丶3 小时前
Golang--锁
linux·开发语言·数据结构·后端·算法·golang·mutex