面试官:在我们的开发过程中,离不开锁,也有很多锁相关的概念,你能梳理下么?
面试者:锁的话有乐观锁、悲观锁、读写锁,还可以基于Mysql、Redis、ZK去实现分布式锁。
面试官:
关于锁的概念有很多,我们可以根据不同的维度去讲。
第一个维度,是从锁的"特性"来分,可以分为乐观锁与悲观锁:
- 悲观锁 认为并发冲突是常态,所以在操作数据前一定要先加锁。它的思想是"先保护,再操作"。
synchronized和ReentrantLock在独占模式下就是典型的悲观锁实现。 - 乐观锁 认为并发冲突是偶然的,所以先直接进行操作,在提交更新时再检测冲突。它的思想是"先操作,有冲突再解决"。最常见的实现方式是版本号机制 或**CAS操作**。Java中的原子类(如
AtomicInteger)就是基于CAS的乐观锁。
第二个维度,是从锁的"设计"与"竞争策略"来分,可以分为公平锁与非公平锁 ,这关系到锁的性能和公平性。
公平锁 遵循先来后到的原则,线程按照申请锁的顺序排队获取。优点是避免了线程饥饿,缺点是整体吞吐量较低,因为要维护一个队列。
非公平锁 允许"插队",新来的线程有机会直接尝试获取锁,获取不到再排队。优点是减少了线程切换的开销,吞吐率高,但可能导致某些线程长时间等待。ReentrantLock可以指定公平或非公平,因为底层的aqs本来就有维护队列,而synchronized是非公平锁。
第三个维度,是关于锁的"状态"和"可重复性", 分为可重入锁与不可重入锁:
可重入锁 允许同一个线程在已经持有锁的情况下,再次获取同一把锁。synchronized和ReentrantLock都是可重入锁。这可以防止线程自己把自己锁死。
不可重入锁则相反,线程不能重复获取锁,如果使用不当就会造成死锁。所以java里面的锁基本上都是可重入锁
第四个维度,是从"共享性"来分,这决定了锁的粒度。 可以分为独占锁(排他锁/写锁)与共享锁(读锁) :
独占锁 一次只允许一个线程访问资源,如ReentrantLock、synchronized。主要用于写操作。
共享锁 允许多个线程同时访问资源,如ReadWriteLock中的ReadLock。主要用于读操作。读写锁(ReentrantReadWriteLock)是这两种策略的经典结合,遵循"读读共享,读写互斥,写写互斥"的原则,能极大提升读多写少场景的性能。
除了以上四个主要维度,在实际的Java并发编程和源码中,我们还会接触到一些更具体、更高级的锁概念:
-
自旋锁 : 当线程尝试获取锁失败时,不会立即挂起,而是循环重试(自旋)。这避免了线程切换的开销,但会消耗CPU。适用于锁被持有时间很短的场景。CAS操作底层就隐含了自旋。
-
自适应自旋锁: 是自旋锁的优化,自旋的时间不再固定,而是由前一次在同一个锁上的自旋成功与否及持有者状态来决定,更加智能。
-
锁消除: 是JIT编译器的优化,当编译器检测到某段代码的锁不可能存在共享数据竞争时,就会将这个锁"消除"掉。
-
锁粗化: 也是编译器的优化。如果虚拟机探测到一连串零碎的操作都对同一个对象反复加锁解锁,会把加锁同步的范围"粗化"到整个操作序列的外部,以减少不必要的性能损耗。
-
偏向锁/轻量级锁/重量级锁 : 这是
synchronized在JVM内部的锁升级机制,是为了在无竞争和低竞争时减少性能开销。 -
-
偏向锁: 适用于只有一个线程访问的场景,直接在对象头标记线程ID,几乎无开销。
-
轻量级锁 : 当有轻微竞争(交替执行)时,通过CAS竞争锁,失败则自旋尝试。
-
重量级锁: 当竞争激烈(自旋失败),会升级为基于操作系统互斥量(Mutex)的锁,线程会进入阻塞队列,进行用户态到内核态的切换,开销最大。
-
集群、微服务、分布式的区别
面试官:我看你的项目也是用的微服务架构,也做了集群部署,工作中也用到了分布式锁啥的。你能跟我讲下集群、微服务、分布式的区别么?
面试者: 集群的话,就是可以部署主从,微服务的话,就是做服务拆分,做解耦,分布式的话 ,服务拆分应该也属于分布式吧。
面试官: OK,这个看似是概念的问题,但是面试官其实更希望结合你的经验、项目来去回答这个问题,因为这些概念在工作中一定能接触到的。
我跟你讲下我的理解。
集群 ,说白了就是"同一个服务,多部署几份"。当一个服务扛不住压力了,我就把它复制出来,在多台机器上多跑几个实例。然后前面挂个负载均衡,把流量均匀分给这些实例。它的核心就是 "复制"和"分摊" ,专门解决 "量不够" 的问题,提升处理能力,防止一个实例挂了导致全瘫。
微服务 ,思路就不同了,它不再是复制,而是 "拆分" 。它把原来一个庞大复杂的单体应用,按照不同的业务功能,拆分成一堆小型、独立的服务。比如拆成用户服务、订单服务、支付服务。每个小服务都高度自治,可以用自己的技术栈,有自己的数据库,能独立开发、测试、部署和伸缩运维。它们之间通过清晰的 API(比如 REST 或 gRPC)来通信协作。它的核心价值是提升 "敏捷性" 和 "可维护性" ,让团队能并行开发,快速迭代,降低系统耦合度。当然,微服务里的每个独立服务,自己也可以用集群的方式来部署,保证它自身的高可用。
分布式 ,这个概念更大,是一个总称。简单理解:只要一个系统的不同组件(或功能)是部署在多台机器/进程上,需要通过网络通信来协同工作,那它就是分布式的 。像分布式锁、事务这些复杂问题,之所以出现,就是因为状态和操作"跨机器"了,不再受单一程序控制。
所以,无论是 集群 (多个相同服务实例协作),还是 微服务 (多个不同服务协作),它们的具体架构模式,都属于 分布式系统 这个大范畴
总结一下关系:
- 集群 :侧重 横向复制扩展,解决单点瓶颈和可用性。
- 微服务 :侧重 业务拆分解耦,解决复杂性和开发效率。
- 分布式 :是涵盖集群、微服务的 总体架构特征。