Java 併發編程 —— 悲觀鎖與樂觀鎖

Java 併發編程 —— 悲觀鎖與樂觀鎖

簡介

爲避免多線程環境下,併發事務造成ACID錯誤,更合理的使用Spring 事務隔離屬性,這篇文章主要介紹如何通過悲觀鎖與樂觀鎖來限制Spring事務隔離屬性使用不當的問題。

悲觀鎖

悲觀鎖(Pessimistic Lock) —— 比較悲觀的鎖。
認爲修改數據時存在着其它連接也想修改此數據的事務。

介紹

  • 悲觀鎖每次獲取數據都要鎖數據(共享資源每次只給一個線程使用)
  • 另外的線程獲取此數據時是block,直到數據被解鎖纔可以使用(其它線程阻塞,用完後再把資源轉讓給其它線程)
  • 常見的悲觀鎖實現(數據庫層面,在以下操作前會鎖住數據)
    • 行鎖
    • 表鎖
    • 讀鎖
    • 寫鎖
  • Java(程序層面,重入鎖與同步鎖都是悲觀鎖的一種實現)
    • ReentrantLock(重入鎖)
    • synchronized(同步鎖)

如何使用

數據庫行鎖

  • FOR UPDATE
# 可在MyBatis使用的時候在SQL中將數據庫記錄鎖住(行鎖)
SELECT CLOUMN_A, CLOUMN_B, CLOUMN_C FROM TABLE WHERE CLOUMN_A = 'PARAM_A' FOR UPDATE
# 上面的SQL鎖住了TABLE中條件爲(CLOUMN_A = 'PARAM_A')的記錄。此事務提交前(提交後會釋放行鎖),其它連接無法使用。
# FOR UPDATE:是行鎖的一個關鍵字,會鎖住這張表的這條記錄,事務提交之後,才允許其它連接使用。

Hibernate 悲觀鎖實現

String sql = "SELECT CLOUMN_A, CLOUMN_B, CLOUMN_C FROM TABLE WHERE CLOUMN_A = 'PARAM_A'";
Query query = session.createQuery(sql);
query.setLockMode("SQL_OBJ",LockModel.UPGRADE);

Hibernate 加鎖模式

  • Hibernate內部使用使用鎖
    • LockMode.NONE
      無鎖機制
    • LockMode.WRITE
      Insert和Update記錄的時候會自動獲取
    • LockMode.READ
      在讀取記錄時會自動獲取
  • 數據庫控制鎖
    • LockMode.UPGRADE
      利用數據庫的for update字句加鎖

注意事項

  • 使用悲觀鎖,必須關閉MySql數據庫自動提交屬性,MySql默認使用autocommit模式,當執行一個更新操作後,MySql會立刻將結果進行提交。 set autocommit = 0;
  • 使用START TRANSACTION,自動提交將保持禁用狀態,直到使用COMMIT或ROLLBACK結束事務。自動提交模式然後恢復到之前的狀態(如果start transaction 前 autocommit = 1,則完成本次事務後autocommit 還是1 。 如果start transaction 前 autocommit = 0, 則完成本次事務後 autocommit 還是0)

樂觀鎖

樂觀鎖(Optimistic Lock) 每次取數據的時候都認爲別人不會修改,所以不鎖。
認爲在短暫的時間裏不會有事務來修改此數據庫的數據!

介紹

  • 大多基於數據版本(Version)
  • 數據版本:數據增加一個版本標識,基於數據庫表的版本解決方案中,一般是通過爲數據表增加一個“version”字段來實現
  • CAS算法實現 (點擊打開 Java算法 —— CAS實現)
  • Java 實現
    • java.util.concurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實現方式CAS實現的。

如何使用

版本標識

  • 讀取數據時,讀取此數據的版本號
  • 更新時,對此版本號加一
  • 提交對版本數據與數據庫表對應記錄的當前版本信息進行對比
  • 如果提交的版本號大於數據庫當前版本,則更新,否則認爲版本過期,事務回滾報錯

注意事項

  • 適用於多讀應用類型,這樣可以提高吞吐量,像數據庫提供的類似於write_condition機制,其實都是提供的樂觀鎖。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章