高级java每日一道面试题-2025年01月22日-JVM篇-乐观锁和悲观锁的理解及如何实现,有哪些实现方式?

如果有遗漏,评论区告诉我进行补充

面试官: 乐观锁和悲观锁的理解及如何实现,有哪些实现方式?

我回答:

一、乐观锁与悲观锁的概念

1. 乐观锁
  • 定义:乐观锁假设并发冲突发生的概率较低,因此不会主动加锁,而是在提交更新时检查是否有其他事务已经修改过数据;如果有,则拒绝提交或提示用户重新尝试。
  • 特点 :
    • 不会阻塞其他线程的操作,允许并发执行。
    • 在更新时通过版本号、时间戳或哈希值等方式验证数据是否被修改。
    • 适用于读多写少的场景,能显著提高并发性能。
2. 悲观锁
  • 定义:悲观锁假设并发冲突发生的概率较高,因此每次操作共享资源时都会加锁,以确保其他线程不能同时访问或修改该资源。
  • 特点 :
    • 加锁期间其他线程必须等待,直到当前持有锁的线程释放锁。
    • 阻塞式操作,可能导致性能瓶颈,特别是在高并发环境下。
    • 适用于写多读少或者对数据一致性要求极高的场景。

二、乐观锁的实现方式

1. 版本号机制
  • 描述:在数据库中为每条数据记录一个版本号字段,在更新操作时比较当前事务的版本号与数据库中的版本号是否一致。
  • 流程 :
    • 如果一致,则执行更新操作,并将版本号递增。
    • 如果不一致,则说明有其他线程已经修改了数据,需要重新尝试更新操作。
  • 示例 (使用JPA的@Version注解):
java 复制代码
@Entity
public class Product {
    @Id
    private Long id;
    
    @Version
    private Integer version;

    // 其他属性...
}
2. 时间戳机制
  • 描述:类似于版本号机制,但使用时间戳来记录数据的最后修改时间。
  • 流程:当执行更新操作时,会比较当前事务的时间戳与数据库中的时间戳是否一致,从而决定是否执行更新操作。
3. CAS(Compare-and-Swap)算法
  • 描述:CAS是一种无锁算法,它通过比较并交换的方式实现对共享变量的原子更新。
  • 流程:在更新数据前检查数据的当前值是否与预期值相等,如果相等则进行更新,否则重新尝试更新操作。
  • 示例 (使用AtomicInteger类):
java 复制代码
AtomicInteger counter = new AtomicInteger(0);

public boolean incrementIfEquals(int expected, int newValue) {
    return counter.compareAndSet(expected, newValue);
}

三、悲观锁的实现方式

1. synchronized 关键字
  • 描述:Java中最常见的实现悲观锁的方式之一。可以在方法或代码块上锁定对象,确保同一时间只有一个线程可以访问共享资源。
  • 示例:
java 复制代码
public synchronized void updateData() {
    // 修改数据的逻辑
}
2. Lock 接口
  • 描述 :Java 5提供了Lock接口来替代synchronized关键字,提供了更灵活的锁机制。
  • 功能:支持公平锁和非公平锁、可中断的获取锁、尝试获取锁等多种功能。
  • 常见实现类 :ReentrantLock等。
  • 示例:
java 复制代码
private final ReentrantLock lock = new ReentrantLock();

public void updateData() {
    lock.lock();
    try {
        // 修改数据的逻辑
    } finally {
        lock.unlock();
    }
}
3. 数据库层面的悲观锁
  • 描述 :在数据库中,悲观锁通常通过SQL语句实现,如使用SELECT ... FOR UPDATE语句来锁定查询到的行,阻止其他事务的读写操作。
  • 示例:
sql 复制代码
SELECT * FROM products WHERE id = ? FOR UPDATE;

四、应用场景

1. 乐观锁的应用场景
  • 适用情况:适用于并发冲突较少、并发性能要求较高的场景。
  • 示例:电商网站的库存扣减,因为库存数据的变化不是非常频繁,可以使用乐观锁来提高并发性能。
2. 悲观锁的应用场景
  • 适用情况:适用于并发冲突较多、数据一致性要求较高的场景。
  • 示例:金融系统的资金转账,由于资金数据的变化非常敏感且频繁,需要使用悲观锁来确保数据的一致性和安全性。

总结

乐观锁和悲观锁是Java并发编程中两个重要的概念,它们各自具有不同的特点和适用场景:

  • 选择合适的锁策略:根据应用的具体需求选择适当的锁策略。对于写多读少或对数据一致性要求极高的场景,可以选择悲观锁;而对于读多写少的场景,乐观锁则更为适合。
  • 结合实际案例:在面试中能够准确阐述这些概念,并结合实际案例讨论其应用场景,将极大地展示你的专业水平和技术深度。
  • 优化性能:理解锁的工作原理可以帮助开发者避免常见的并发问题,并写出更高效的并发代码。例如,在高并发环境中,合理运用乐观锁可以大大提升系统的吞吐量和响应速度。
相关推荐
2601_9621066430 分钟前
【Java Set综合实战】商品管理系统 + 图书收藏系统|Product、Book自定义类全套代码
java
RuoyiOffice1 小时前
SpringBoot+Vue3 凭证生命周期设计:审核、过账、冲销与反过账怎样保持账目一致
java·spring boot·事务·springboot3·财务系统·会计凭证·ruoyi office
2601_962106642 小时前
HashMap入门|工号-姓名键值存储、两种遍历方式、根据key/value查
java
杨凯凡2 小时前
Spring Framework 7 API 版本管理实战:请求头路由、兼容迁移与废弃策略
java·rest api·springframework·spring boot 4·api版本管理
T_Wizard3 小时前
同一个问题写了 4 遍:官方 SDK / HTTP / Spring AI / LangChain4j,到底该选哪个
java·ai编程
hai_android3 小时前
AudioTrack 深度解析:从原理到实战
android·java·kotlin
斯维赤3 小时前
限流、熔断、降级,你真的分得清吗?
java·后端·架构
aiks_yuan6 小时前
Java CPU 100% 怎么排查:JDK 11 起 jstack 自带线程 CPU 时间,不用再 top -H 换算十六进制(JDK 8、11、17 实测)
java
Flynn Liang7 小时前
AiServices 源码级拆解:一个接口如何变成 RAG Agent
java·langchain·源码分析·rag·langchain4j·aiservices
Flynn Liang7 小时前
LangChain4j 1.23 架构全景:Java 大模型应用的 6 层抽象
java·langchain·大模型·rag·ai agent·langchain4j