悲觀鎖
1、使用場景:
悲觀鎖可以作爲分佈式鎖的一種實現方式,即你某些業務想在高併發的場景下仍被單機執行時,可以在業務代碼執行前,先去獲取某行數據的悲觀鎖,執行業務完成後釋放鎖(commit or rollback),當你還沒有釋放鎖之前,如果有其他線程執行進來且要獲取相同表相同行數據的悲觀鎖,肯定是獲取失敗的,會拋出異常,而不會去執行業務代碼。
2、悲觀鎖的核心實現:
select id from table where ... for update nowait
- 此語句如果會把所有符合條件的行鎖住,如果沒有where條件會鎖整張表,如果查詢條件獲取到的行數據爲空,則不會鎖住行數據;
- 一旦行數據被這樣鎖住了,如果獲取悲觀鎖的當前事務還沒有commit,那麼下一個獲取悲觀鎖的事務一定是失敗的;
- 如果鎖用完了,需要釋放 (即事務的 commit 或 rollback ) 。mybatis框架會自動commit,即在獲取完悲觀鎖以後立即就commit了,而沒有等到業務執行完才 commit,在高併發時都能獲取悲觀鎖成功。所以需要使用編稱式事務來替代 @Transacional
- nowait表示:不會被阻塞,如果獲取悲觀鎖失敗,直接拋異常而不必等待。
3、測試1
測試環境:oracle數據庫 + mybatis
釋放悲觀鎖,需要執行 commit 。
4、測試2
5、代碼實現
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
TransactionStatus status = null;
try {
//開啓編稱式事務
status = txManager.getTransaction(new DefaultTransactionDefinition());
//獲取悲觀鎖
funcScheduleDefService.forUpdateNoWait(monthendConfirmDTO.getDid(), function, monthendConfirmDTO.getStatDate());
} catch (Exception e) {
//獲取鎖失敗,回滾事務
txManager.rollback(status);
return null;
}
。。。添加悲觀鎖成功,執行業務邏輯
//執行業務完成,提交事務,釋放鎖
txManager.commit(status);