Springboot在有鎖的情況下如何正確使用事務

追風人聊Java發表於2021-10-09

1. 概述

老話說的好:想要賺錢,就去看看有錢人有什麼需求,因為有錢人錢多,所以賺的多。

 

言歸正傳,在Java專案的研發中,“鎖”這個詞並不陌生,最經典的使用場景是商品的超賣問題。

很多Java小白,通常會認為,給程式碼加上一把“鎖”,就能解決多扣庫存問題,卻忽略了資料庫事務的問題,今天我們就來做一個實驗,分析一下商品超賣問題。

 

2. 場景介紹

有一款商品,庫存只剩1件。

購買商品時,做三個動作,一是檢查庫存,二是扣庫存,三是生成訂單,三個動作在一個事務中執行。

模擬併發場景,使用10個執行緒同時執行使用者購買商品的操作。

 

3. 程式碼實現

3.1 不加鎖的程式碼實現

    @Transactional(rollbackFor = Exception.class)
    public void buy() {

        // 檢視是商品否有庫存
        Integer count = getProductCount();
        if(count <= 0) {
            throw new RuntimeException("庫存為 0");
        }

        // 減庫存
        productRepository.reductCount();

        // 生成訂單
        createOrder();
    }

 

首先我們正常編寫業務邏輯,用 @Transactional 註解控制事務。

經併發實驗,產生了超賣的現象。

 

3.2 加鎖的程式碼實現

    @Transactional(rollbackFor = Exception.class)
    public synchronized void buy() {

        // 檢視是商品否有庫存
        Integer count = getProductCount();
        if(count <= 0) {
            throw new RuntimeException("庫存為 0");
        }

        // 減庫存
        productRepository.reductCount();

        // 生成訂單
        createOrder();
    }

 

這次我們使用 synchronized 關鍵字給方法加了把“鎖”,理論上應該不會產生超賣現象了吧。

經實驗,仍然產生了超賣現象。

因為雖然方法被鎖住了,可是 @Transactional 註解 並沒有及時的提交事務,導致庫存沒有及時扣減為0,因此還是超賣了。

 

3.3 正確使用事務的程式碼實現

    @Autowired
    private PlatformTransactionManager platformTransactionManager;
    @Autowired
    private TransactionDefinition transactionDefinition;

    // @Transactional(rollbackFor = Exception.class)
    public synchronized void buy() {

        // 開啟事務
        TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);

        try {
            // 檢視是商品否有庫存
            Integer count = getProductCount();
            if(count <= 0) {
                throw new RuntimeException("庫存為 0");
            }

            // 減庫存
            productRepository.reductCount();

            // 生成訂單
            createOrder();

            // 事務提交
            platformTransactionManager.commit(transactionStatus);
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
            // 事務回滾
            platformTransactionManager.rollback(transactionStatus);
        }

    }

 

這次我們不使用 @Transactional 註解管理事務了,改為手動管理事務。

經實驗,解決了超賣現象。

 

4. 綜述

今天聊了一下 Springboot在有鎖的情況下如何正確使用事務,希望可以對大家的工作有所幫助。

歡迎幫忙點贊、評論、轉發、加關注 :)

關注追風人聊Java,每天更新Java乾貨。

 

相關文章