最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

當(dāng)Transactional遇上synchronized的解決方法分享

 更新時間:2023年05月07日 14:08:17   作者:思想領(lǐng)袖  
前些時間剛好刷到了有關(guān)于“# 【事務(wù)與鎖】當(dāng)Transactional遇上synchronized”這一類的文章,感覺這也是工作中經(jīng)常會遇到的一類問題了。所以就針對這個話題進行了分析并整理了常用的解決方法,希望對大家有所幫助

問題情形

假設(shè)代碼如下:

//controller層:
@GetMapping("/t1")
@Transactional(rollbackFor = Exception.class)
public void getTest1() {
    String n = countNumService.getCount();
    System.out.println(" t1 : " + n);
    try {
        Thread.sleep(60000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
}

@GetMapping("/t2")
@Transactional(rollbackFor = Exception.class)
public void getTest2() {
    String n = countNumService.getCount();
    System.out.println(" t2 : " + n);
    // 忽略其他的增刪操作
}

//service層:
@Override
@Transactional(rollbackFor = Exception.class)
public synchronized String getCount() {
    // 獲取
    CountNum countNum = countNumMapper.selectById(1);

    countNum.setNumber(countNum.getNumber() + 1);
    // 修改
    countNumMapper.updateById(countNum);

    return countNum.getNumber().toString();
}

問題分析

首先,在 getTest1()getTest2() 這兩個方法中都加了 @Transactional 注解,因此它們會分別開啟自己的事務(wù)。假設(shè)在某一刻,兩個線程同時調(diào)用了 /t1/t2 接口,并且 /t1 接口中執(zhí)行了較長的睡眠操作,于是 /t2 的業(yè)務(wù)邏輯率先完成,并且更新了數(shù)據(jù)庫中的Number字段。

隨后 /t1 的業(yè)務(wù)邏輯也完成了,但是由于之前被阻塞了 60 秒鐘,此時讀取到的計數(shù)器值已經(jīng)過期了,不能反映最新的狀態(tài)。因此 /t1 返回的結(jié)果將是過期的數(shù)據(jù),與 /t2 返回的結(jié)果不一致。所以這段代碼存在一個并發(fā)問題,可能導(dǎo)致數(shù)據(jù)的不一致性。

解決方法

這里我給出常用的解決方法:

  • getCount() 方法上的 synchronized 去掉,使用樂觀鎖的方式來控制并發(fā)訪問。
  • /t1/t2 兩個接口的事務(wù)設(shè)置為同一個事務(wù),即兩個接口共享同一個事務(wù)上下文??梢酝ㄟ^ Spring 的聲明式事務(wù)管理機制來實現(xiàn)。(在 Spring 框架中,我們可以使用 @Transactional 注解來聲明式地管理事務(wù)。使用 PROPAGATION_REQUIRED 屬性可以表示當(dāng)前方法需要加入到一個存在的事務(wù)中,如果不存在,則開啟新的事務(wù)。)
  • getCount() 方法中,進行增量更新,而不是直接把Number字段加 1,例如使用 update count_num set number = number + 1 where id = ? 等 SQL 語句來實現(xiàn)。
  • 保留synchronized關(guān)鍵字的情況下,添加事務(wù)管理器進行手動事務(wù)管理。

代碼參考

1.樂觀鎖方案

@Override
@Transactional(rollbackFor = Exception.class)
public String getCount() {
    CountNum countNum = countNumMapper.selectById(1);
    // 使用版本號作為樂觀鎖
    int version = countNum.getVersion();
    countNum.setNumber(countNum.getNumber() + 1);
    countNum.setVersion(version + 1);
    // 更新操作必須要包含版本號字段
    int rows = countNumMapper.updateById(countNum);
    if (rows == 0) {
        throw new OptimisticLockException("事務(wù)中更新失敗");
    }
    return countNum.getNumber().toString();
}

2.將 /t1 和 /t2 兩個接口的事務(wù)設(shè)置為同一個事務(wù)。在 CountNumService 類的事務(wù)注解上添加了 propagation = Propagation.REQUIRED 屬性,表示當(dāng)前方法需要加入到一個已存在的事務(wù)中。此時,如果 /t1/t2 調(diào)用的是同一個 CountNumService 實例,則它們將共享同一個事務(wù)上下文。

@Service
public class CountNumService {
    @Autowired
    private CountNumMapper countNumMapper;
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public String getCount() {
         // 與之前代碼相同
    }
}
@RestController
public class CountNumController {
    @Autowired
    private CountNumService countNumService;
    @GetMapping("/t1")
    public void getTest1() {
        String n = countNumService.getCount();
        System.out.println(" t1 : " + n);
        try {
            Thread.sleep(60000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    @GetMapping("/t2")
    public void getTest2() {
        String n = countNumService.getCount();
        System.out.println(" t2 : " + n);
        // 忽略其他的增刪操作
    }
}

3.SQL中增量更新方案

@Mapper
public interface CountNumMapper {
    @Update("UPDATE count_num SET number = number + 1 WHERE id = 1")
    int updateNumber();
}
@Override
@Transactional(rollbackFor = Exception.class)
public String getCount() {
    // 直接執(zhí)行 SQL 語句進行增量更新操作
    countNumMapper.updateNumber();
    // 再查詢一次獲取最新值
    CountNum countNum = countNumMapper.selectById(1);
    return countNum.getNumber().toString();
}

4.手動事務(wù)管理方案

@Service
public class CountNumService {
    @Autowired
    private CountNumMapper countNumMapper;

    // 使用spring事務(wù)管理器
    @Autowired
    private PlatformTransactionManager transactionManager;

    public synchronized String getCount() {
        TransactionStatus status = null;
        try {
            DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
            //將傳播行為設(shè)置為 PROPAGATION_REQUIRED,以確保當(dāng)前方法在事務(wù)內(nèi)執(zhí)行:
            definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
            //獲取當(dāng)前事務(wù)的狀態(tài)信息:
            status = transactionManager.getTransaction(definition);
            
            CountNum countNum = countNumMapper.selectById(1);
            countNum.setNumber(countNum.getNumber() + 1);

            int rows = countNumMapper.updateById(countNum);
            if (rows == 0) {
                throw new RuntimeException("事務(wù)中更新失敗");
            }
            //提交事務(wù):
            transactionManager.commit(status);
            return countNum.getNumber().toString();
        } catch (Exception e) {
            if (status != null) {
                //回滾事務(wù):
                transactionManager.rollback(status);
            }
            throw e;
        }
    }
}

到此這篇關(guān)于當(dāng)Transactional遇上synchronized的解決方法分享的文章就介紹到這了,更多相關(guān)Transactional synchronized內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot 引?MybatisGenerator的實現(xiàn)步驟

    SpringBoot 引?MybatisGenerator的實現(xiàn)步驟

    本文主要介紹了SpringBoot 引?MybatisGenerator的實現(xiàn)步驟,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-02-02
  • java高級用法之注解和反射講義

    java高級用法之注解和反射講義

    這篇文章主要給大家介紹了關(guān)于java高級用法之注解和反射講義的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • Mybatis-Plus Wrapper條件構(gòu)造器超詳細使用教程

    Mybatis-Plus Wrapper條件構(gòu)造器超詳細使用教程

    接口方法的參數(shù)中,會出現(xiàn)各種 Wrapper,比如 queryWrapper、updateWrapper 等。Wrapper 的作用就是用于定義各種各樣的條件(where)。所以不管是查詢、更新、刪除都會用到Wrapper
    2022-03-03
  • 顯示IntelliJ IDEA工具的Run Dashboard功能圖文詳解

    顯示IntelliJ IDEA工具的Run Dashboard功能圖文詳解

    這篇文章主要介紹了顯示IntelliJ IDEA工具的Run Dashboard功能,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-07-07
  • 基于Java實現(xiàn)的Dijkstra算法示例

    基于Java實現(xiàn)的Dijkstra算法示例

    這篇文章主要介紹了基于Java實現(xiàn)的Dijkstra算法示例,一個比較典型的算法示例,需要的朋友可以參考下
    2014-07-07
  • Java實現(xiàn)對象排序的兩種方式詳解

    Java實現(xiàn)對象排序的兩種方式詳解

    這篇文章主要介紹了Java實現(xiàn)對象排序的兩種方式詳解,在Java中經(jīng)常會涉及到對象數(shù)組的排序問題,則就提到對象之間的比較問題,今天我們就來看一下兩種不同排序方式之間的區(qū)別,需要的朋友可以參考下
    2023-09-09
  • 關(guān)于Gateway網(wǎng)關(guān)中配置跨域的三種方案

    關(guān)于Gateway網(wǎng)關(guān)中配置跨域的三種方案

    文章總結(jié):介紹了三種處理跨域請求的方法:在Controller類上添加注解、通過配置類實現(xiàn)重寫WebMvcConfigurer接口和在配置文件中統(tǒng)一設(shè)置,希望這些方法能幫助讀者解決跨域問題
    2024-11-11
  • Java魔法堂之調(diào)用外部程序的方法

    Java魔法堂之調(diào)用外部程序的方法

    這篇文章主要介紹了Java魔法堂:調(diào)用外部程序的相關(guān)知識,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2021-02-02
  • springAop實現(xiàn)講解(看這篇夠了)

    springAop實現(xiàn)講解(看這篇夠了)

    AOP面向切面編程是一種編程范式,它通過將通用的橫切關(guān)注點(如日志、事務(wù)、權(quán)限控制等)與業(yè)務(wù)邏輯分離,使得代碼更加清晰、簡潔、易于維護,這篇文章主要介紹了springAop實現(xiàn)講解(看這篇夠了),需要的朋友可以參考下
    2024-02-02
  • springboot3解決跨域的幾種方式小結(jié)

    springboot3解決跨域的幾種方式小結(jié)

    這篇文章主要介紹了springboot3解決跨域的幾種方式,文中通過代碼示例給大家介紹的非常詳細,對大家的解決跨域有一定的幫助,需要的朋友可以參考下
    2024-03-03

最新評論

神木县| 方城县| 安西县| 龙川县| 普宁市| 恩施市| 军事| 军事| 勐海县| 东宁县| 汉中市| 深州市| 云安县| 荥经县| 双江| 武邑县| 韶关市| 呼伦贝尔市| 宕昌县| 临颍县| 革吉县| 安阳县| 揭东县| 巫溪县| 秦皇岛市| 新野县| 冕宁县| 崇礼县| 宝清县| 盱眙县| 扎兰屯市| 清丰县| 建宁县| 清水河县| 栾城县| 永德县| 秭归县| 尚志市| 湟中县| 泰安市| 井冈山市|