一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Spring Boot2+JPA之悲觀鎖和樂觀鎖實戰教程

Spring Boot2+JPA之悲觀鎖和樂觀鎖實戰教程

2022-02-20 12:08Moshow鄭鍇 Java教程

這篇文章主要介紹了Spring Boot2+JPA之悲觀鎖和樂觀鎖實戰教程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

前言

大量的請求,或者同時的操作,容易導致系統在業務上發生并發的問題. 通常講到并發,解決方案無非就是前端限制重復提交,后臺進行悲觀鎖或者樂觀鎖限制.

悲觀鎖與并發

悲觀鎖(Pessimistic Lock),顧名思義,就是很悲觀,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會block直到解鎖,可以理解為獨占鎖。在java中synchronized和ReentrantLock重入鎖等鎖就是悲觀鎖,數據庫中表鎖、行鎖、讀寫鎖等也是悲觀鎖。

利用SQL的for update解決并發問題

行鎖就是操作數據的時候把這一行數據鎖住,其他線程想要讀寫必須等待,但同一個表的其他數據還是能被其他線程操作的。只要在需要查詢的sql后面加上for update,就能鎖住查詢的行,特別要注意查詢條件必須要是索引列,如果不是索引就會變成表鎖,把整個表都鎖住。

?
1
2
3
4
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

  • NONE: No lock.
  • OPTIMISTIC: Optimistic lock.
  • OPTIMISTIC_FORCE_INCREMENT: Optimistic lock, with version update.
  • PESSIMISTIC_FORCE_INCREMENT: Pessimistic write lock, with version update.
  • PESSIMISTIC_READ: Pessimistic read lock.
  • PESSIMISTIC_WRITE: Pessimistic write lock.
  • READ: Synonymous with OPTIMISTIC.
  • WRITE: Synonymous with OPTIMISTIC_FORCE_INCREMENT.
?
1
2
3
4
5
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,則可以

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

如果用entityManager的方式,則可以設置LocakMode:

?
1
2
3
4
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到數據庫了,進行報錯把,這樣就避免了并發時數據沖突的問題。

?
1
2
3
4
5
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);
}
?
1
2
3
4
5
6
7
8
9
10
11
12
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方式實現樂觀鎖。

  • each entity class must have only one version attribute .每個實體類只能有一個@Version字段,不能多
  • it must be placed in the primary table for an entity mapped to several tables . 對于映射到多個表的實體,必須將其放置在主表中
  • type of a version attribute must be one of the following: int, Integer, long, Long, short, Short, java.sql.Timestamp ,

@Version支持的類型必須是以下類型:

  • int
  • Integer
  • long
  • Long
  • short
  • Short
  • java.sql.Timestamp

首先在Article實體類的version字段上加上@Version注解

?
1
2
3
4
5
6
7
8
9
@Data
@Entity
public class Article{
    @Id
    private Long id; 
  //...... 
    @Version
    private Integer version;
}
?
1
2
3
Article article = entityManager.find(Article.class, id);
entityManager.lock(article , LockModeType.OPTIMISTIC);
entityManager.refresh(article , LockModeType.READ);

什么時候用悲觀鎖或者樂觀鎖

悲觀鎖適合寫多讀少的場景。因為在使用的時候該線程會獨占這個資源,就適合用悲觀鎖,否則用戶只是瀏覽文章的話,用悲觀鎖就會經常加鎖,增加了加鎖解鎖的資源消耗。

樂觀鎖適合寫少讀多的場景。由于樂觀鎖在發生沖突的時候會回滾或者重試,如果寫的請求量很大的話,就經常發生沖突,結合事務會有經常的回滾和重試,這樣對系統資源消耗也是非常大。

所以悲觀鎖和樂觀鎖沒有絕對的好壞,必須結合具體的業務情況來決定使用哪一種方式。另外在阿里巴巴開發手冊里也有提到:

如果每次訪問沖突概率小于 20%,推薦使用樂觀鎖,否則使用悲觀鎖。樂觀鎖的重試次數不得小于3次。

阿里巴巴建議以沖突概率20%這個數值作為分界線來決定使用樂觀鎖和悲觀鎖,雖然說這個數值不是絕對的,但是作為阿里巴巴各個大佬總結出來的也是一個很好的參考。

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。

原文鏈接:https://zhengkai.blog.csdn.net/article/details/103086074

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 欧美精品成人a多人在线观看 | 精品一成人岛国片在线观看 | 奇米777狠狠 | 美女班主任下面好爽好湿好紧 | 暖暖日本在线观看免费 | 闺蜜调教我做她的脚奴 | 香蕉免费看一区二区三区 | 亚洲婷婷在线视频 | 国产微拍精品一区 | 午夜片神马影院福利 | 星星动漫无删减在线观看 | 午夜伦伦电影理论片费看 | 天天舔天天操天天干 | 2018高清国产一道国产 | 欧美在线成人免费国产 | 四虎成人免费观看在线网址 | 欧洲美女女同 | 无码人妻99久久密AV | 亚洲国产欧美久久香综合 | 91免费永久在线地址 | 亚洲AV国产福利精品在现观看 | 亚洲九九精品 | 9191久久| 国产午夜大片 | 狠狠撸在线影院 | www.四虎在线观看 | 免费草比视频 | 福利视频一区青娱 | 亚洲高清一区二区三区久久 | 国产九九视频在线观看 | 亚洲激情在线视频 | 大胆人gogo888体艺术在线 | 日韩综合网 | 掀开奶罩边躁狠狠躁软学生 | 久久精品动漫网一区二区 | 国产盗摄wc女厕所 | 国内久久| 高清视频免费 | 美女林柏欣21p人体之仓之梦 | a∨在线观看 | 2021日产国产麻豆 |