文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Spring Boot2+JPA之悲观锁和乐观锁实战教程

2024-04-02 19:55

关注

前言

大量的请求,或者同时的操作,容易导致系统在业务上发生并发的问题. 通常讲到并发,解决方案无非就是前端限制重复提交,后台进行悲观锁或者乐观锁限制.

悲观锁与并发

悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到解锁,可以理解为独占锁。在java中synchronized和ReentrantLock重入锁等锁就是悲观锁,数据库中表锁、行锁、读写锁等也是悲观锁。

利用SQL的for update解决并发问题

行锁就是操作数据的时候把这一行数据锁住,其他线程想要读写必须等待,但同一个表的其他数据还是能被其他线程操作的。只要在需要查询的sql后面加上for update,就能锁住查询的行,特别要注意查询条件必须要是索引列,如果不是索引就会变成表锁,把整个表都锁住。


public interface ArticleRepository extends JpaRepository<Article, Long> {
    @Query(value = "select * from article a where a.id = :id for update", nativeQuery = true)
    Optional<Article> findArticleForUpdate(Long id);
}

利用JPA的@Lock行锁注解解决并发问题

如果说for update的做法太原始,那么JPA有提供一个更加优雅的方法,就是@Lock注解 .

为Repository添加JPA的锁方法,其中LockModeType.PESSIMISTIC_WRITE参数就是行锁。

关于LockModeType这个类型,可以在这找到文档 https://docs.oracle.com/javaee/7/api/javax/persistence/LockModeType.html


public interface ArticleRepository extends JpaRepository<Article, Long> { 
    @Lock(value = LockModeType.PESSIMISTIC_WRITE)
    @Query("select a from Article a where a.id = :id")
    Optional<Article> findArticleWithPessimisticLock(Long id);
}

如果是@NameQuery,则可以


@NamedQuery(name="lockArticle",query="select a from Article a where a.id = :id",lockMode = PESSIMISTIC_READ)
public class Article

如果用entityManager的方式,则可以设置LocakMode:


 Query query = entityManager.createQuery("from Article where articleId = :id");
 query.setParameter("id", id);
 query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
 query.getResultList();

乐观锁与并发

乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去修改。所以悲观锁是限制其他线程,而乐观锁是限制自己,虽然他的名字有锁,但是实际上不算上锁,通常为version版本号机制,还有CAS算法 .

利用version字段解决并发问题

版本号机制就是在数据库中加一个字段version当作版本号。那么获取Article的时候就会带一个版本号,比如version=1,然后你对这个Article一波操作,操作完之后要插入到数据库了。

校验一下version版本号,发现在数据库里对应Article记录的version=2,这和我手里的版本不一样啊,说明提交的Article不是最新的,那么就不能update到数据库了,进行报错把,这样就避免了并发时数据冲突的问题。


public interface ArticleRepository extends JpaRepository<Article, Long> {
    @Modifying
    @Query(value = "update article set content= :content, version = version + 1 where id = :id and version = :version", nativeQuery = true)
    int updateArticleWithVersion(Long id, String content, Long version);
}

public void postComment(Long articleId, String content) {
 //get article
    Optional<Article> articleOptional = articleRepository.findById(articleId);
    //update with Optimistic Lock
    int count = articleRepository.updateArticleWithVersion(article.getId(), content, article.getVersion());
  
    if (count == 0) {
        throw new RuntimeException("更新数据失败,请刷新重试");
    }else{
     articleRepository.save(article);
    } 
}

利用JPA的@Version版本机制解决并发问题

有没有更优雅的方式? 当然,必须有,那就是JPA自带的@Version方式实现乐观锁。

@Version支持的类型必须是以下类型:

首先在Article实体类的version字段上加上@Version注解


@Data
@Entity
public class Article{ 
    @Id
    private Long id;  
  //......  
    @Version
    private Integer version; 
}

Article article = entityManager.find(Article.class, id);
entityManager.lock(article , LockModeType.OPTIMISTIC);
entityManager.refresh(article , LockModeType.READ);

什么时候用悲观锁或者乐观锁

悲观锁适合写多读少的场景。因为在使用的时候该线程会独占这个资源,就适合用悲观锁,否则用户只是浏览文章的话,用悲观锁就会经常加锁,增加了加锁解锁的资源消耗。

乐观锁适合写少读多的场景。由于乐观锁在发生冲突的时候会回滚或者重试,如果写的请求量很大的话,就经常发生冲突,结合事务会有经常的回滚和重试,这样对系统资源消耗也是非常大。

所以悲观锁和乐观锁没有绝对的好坏,必须结合具体的业务情况来决定使用哪一种方式。另外在阿里巴巴开发手册里也有提到:

如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于3次。

阿里巴巴建议以冲突概率20%这个数值作为分界线来决定使用乐观锁和悲观锁,虽然说这个数值不是绝对的,但是作为阿里巴巴各个大佬总结出来的也是一个很好的参考。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯