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

Spring事務(wù)管理下synchronized鎖失效問(wèn)題的解決方法

 更新時(shí)間:2022年03月31日 16:18:08   作者:臥蟲(chóng)呀  
這篇文章主要給大家介紹了關(guān)于Spring事務(wù)管理下synchronized鎖失效問(wèn)題的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

最近看到一個(gè)技術(shù)技術(shù)問(wèn)題:synchronized鎖問(wèn)題?

開(kāi)啟10000個(gè)線(xiàn)程,每個(gè)線(xiàn)程給員工表的money字段【初始值是0】加1,沒(méi)有使用悲觀鎖和樂(lè)觀鎖,但是在業(yè)務(wù)層方法上加了synchronized關(guān)鍵字,問(wèn)題是代碼執(zhí)行完畢后數(shù)據(jù)庫(kù)中的money 字段不是10000,而是小于10000 問(wèn)題出在哪里?

Service層代碼:

SQL代碼(沒(méi)有加悲觀/樂(lè)觀鎖):

用1000個(gè)線(xiàn)程跑代碼:

簡(jiǎn)單來(lái)說(shuō):多線(xiàn)程跑一個(gè)使用synchronized關(guān)鍵字修飾的方法,方法內(nèi)操作的是數(shù)據(jù)庫(kù),按正常邏輯應(yīng)該最終的值是1000,但經(jīng)過(guò)多次測(cè)試,結(jié)果是低于1000。這是為什么呢?

一、我的思考

既然測(cè)試出來(lái)的結(jié)果是低于1000,那說(shuō)明這段代碼不是線(xiàn)程安全的。不是線(xiàn)程安全的,那問(wèn)題出現(xiàn)在哪呢?眾所周知,synchronized方法能夠保證所修飾的代碼塊、方法保證有序性、原子性、可見(jiàn)性。

講道理,以上的代碼跑起來(lái),問(wèn)題中Service層的increaseMoney()是有序的、原子的、可見(jiàn)的,所以斷定跟synchronized應(yīng)該沒(méi)關(guān)系。

既然Java層面上找不到原因,那分析一下數(shù)據(jù)庫(kù)層面的吧(因?yàn)榉椒▋?nèi)操作的是數(shù)據(jù)庫(kù))。在increaseMoney()方法前加了@Transcational注解,說(shuō)明這個(gè)方法是帶有事務(wù)的。事務(wù)能保證同組的SQL要么同時(shí)成功,要么同時(shí)失敗。講道理,如果沒(méi)有報(bào)錯(cuò)的話(huà),應(yīng)該每個(gè)線(xiàn)程都對(duì)money值進(jìn)行+1。從理論上來(lái)說(shuō),結(jié)果應(yīng)該是1000的才對(duì)。

根據(jù)上面的分析,我懷疑是提問(wèn)者沒(méi)測(cè)試好(hhhh,逃),于是我也跑去測(cè)試了一下,發(fā)現(xiàn)是以提問(wèn)者的方式來(lái)使用是真的有問(wèn)題。

首先貼一下我的測(cè)試代碼:

@RestController
public class EmployeeController {
    @Autowired
    private EmployeeService employeeService;
    @RequestMapping("/add")
    public void addEmployee() {
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> employeeService.addEmployee()).start();
        }
    }
}
@Service
public class EmployeeService {
    @Autowired
    private EmployeeRepository employeeRepository;
    @Transactional
    public synchronized void addEmployee() {
        // 查出ID為8的記錄,然后每次將年齡增加一
        Employee employee = employeeRepository.getOne(8);
        System.out.println(employee);
        Integer age = employee.getAge();
        employee.setAge(age + 1);
        employeeRepository.save(employee);
    }
}

簡(jiǎn)單地打印了每次拿到的employee值,并且拿到了SQL執(zhí)行的順序,如下(貼出小部分):

如下(貼出小部分):

從打印的情況我們可以得出:多線(xiàn)程情況下并沒(méi)有串行執(zhí)行addEmployee()方法。這就導(dǎo)致對(duì)同一個(gè)值做重復(fù)的修改,所以最終的數(shù)值比1000要少。

二、圖解出現(xiàn)的原因

發(fā)現(xiàn)并不是同步執(zhí)行的,于是我就懷疑synchronized關(guān)鍵字和Spring肯定有點(diǎn)沖突。于是根據(jù)這兩個(gè)關(guān)鍵字搜了一下,找到了問(wèn)題所在。

我們知道Spring事務(wù)的底層是Spring AOP,而Spring AOP的底層是動(dòng)態(tài)代理技術(shù)。跟大家一起回顧一下動(dòng)態(tài)代理:

 public static void main(String[] args) {
        // 目標(biāo)對(duì)象
        Object target ;
        Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), Main.class, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 但凡帶有@Transcational注解的方法都會(huì)被攔截
                // 1... 開(kāi)啟事務(wù)
                method.invoke(target);
                // 2... 提交事務(wù)
                return null;
            }
            
        });
    }

實(shí)際上Spring做的處理跟以上的思路是一樣的,我們可以看一下TransactionAspectSupport類(lèi)中invokeWithinTransaction():

調(diào)用方法前開(kāi)啟事務(wù),調(diào)用方法后提交事務(wù)

在多線(xiàn)程環(huán)境下,就可能會(huì)出現(xiàn):方法執(zhí)行完了(synchronized代碼塊執(zhí)行完了),事務(wù)還沒(méi)提交,別的線(xiàn)程可以進(jìn)入被synchronized修飾的方法,再讀取的時(shí)候,讀到的是還沒(méi)提交事務(wù)的數(shù)據(jù),這個(gè)數(shù)據(jù)不是最新的,所以就出現(xiàn)了這個(gè)問(wèn)題。

三、解決問(wèn)題

從上面我們可以發(fā)現(xiàn),問(wèn)題所在是因?yàn)锧Transcational注解和synchronized一起使用了,加鎖的范圍沒(méi)有包括到整個(gè)事務(wù)。所以我們可以這樣做:

新建一個(gè)名叫SynchronizedService類(lèi),讓其去調(diào)用addEmployee()方法,整個(gè)代碼如下:

@RestController
public class EmployeeController {
    @Autowired
    private SynchronizedService synchronizedService ;
    @RequestMapping("/add")
    public void addEmployee() {
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> synchronizedService.synchronizedAddEmployee()).start();
        }
    }
}
// 新建的Service類(lèi)
@Service
public class SynchronizedService {
    @Autowired
    private EmployeeService employeeService ;
	
    // 同步
    public synchronized void synchronizedAddEmployee() {
        employeeService.addEmployee();
    }
}
@Service
public class EmployeeService {
    @Autowired
    private EmployeeRepository employeeRepository;
    
    @Transactional
    public void addEmployee() {
        // 查出ID為8的記錄,然后每次將年齡增加一
        Employee employee = employeeRepository.getOne(8);
        System.out.println(Thread.currentThread().getName() + employee);
        Integer age = employee.getAge();
        employee.setAge(age + 1);
        employeeRepository.save(employee);
    }
}

我們將synchronized鎖的范圍包含到整個(gè)Spring事務(wù)上,這就不會(huì)出現(xiàn)線(xiàn)程安全的問(wèn)題了。在測(cè)試的時(shí)候,我們可以發(fā)現(xiàn)1000個(gè)線(xiàn)程跑起來(lái)比之前要慢得多,當(dāng)然我們的數(shù)據(jù)是正確的:

拋開(kāi)上面事務(wù)造成的synchronized失效問(wèn)題,synchronized本身是悲觀鎖,代價(jià)偏高,像數(shù)據(jù)庫(kù)數(shù)據(jù)修改的線(xiàn)程安全問(wèn)題,可以使用樂(lè)觀鎖,在表中添加version字段,每次修改時(shí)預(yù)期值與數(shù)據(jù)庫(kù)值比較,失敗的話(huà)一定次數(shù)自旋嘗試修改,修改成功的話(huà)version+1。

總結(jié)

到此這篇關(guān)于Spring事務(wù)管理下synchronized鎖失效問(wèn)題的文章就介紹到這了,更多相關(guān)Spring事務(wù)synchronized鎖失效內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java Socket編程筆記_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Java Socket編程筆記_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Socket對(duì)于我們來(lái)說(shuō)就非常實(shí)用了。下面是本次學(xué)習(xí)的筆記。主要分異常類(lèi)型、交互原理、Socket、ServerSocket、多線(xiàn)程這幾個(gè)方面闡述
    2017-05-05
  • 深入理解Java中HashCode方法

    深入理解Java中HashCode方法

    這篇文章主要介紹了深入理解Java中HashCode方法,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • 深入理解Java設(shè)計(jì)模式之職責(zé)鏈模式

    深入理解Java設(shè)計(jì)模式之職責(zé)鏈模式

    這篇文章主要介紹了JAVA設(shè)計(jì)模式之職責(zé)鏈模式的的相關(guān)資料,文中示例代碼非常詳細(xì),供大家參考和學(xué)習(xí),感興趣的朋友可以了解
    2021-11-11
  • springboot使用消息中間件

    springboot使用消息中間件

    這篇文章主要介紹了springboot使用消息中間件,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • SpringBoot+Vue項(xiàng)目部署上線(xiàn)的實(shí)現(xiàn)示例

    SpringBoot+Vue項(xiàng)目部署上線(xiàn)的實(shí)現(xiàn)示例

    本文主要介紹了SpringBoot+Vue項(xiàng)目部署上線(xiàn)的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-02-02
  • Java實(shí)現(xiàn)自定義枚舉值校驗(yàn)器的示例代碼

    Java實(shí)現(xiàn)自定義枚舉值校驗(yàn)器的示例代碼

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)自定義枚舉值校驗(yàn)器的相關(guān)資料,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以參考一下
    2023-02-02
  • java中Hutool包的常用方法總結(jié)

    java中Hutool包的常用方法總結(jié)

    這篇文章主要為大家詳細(xì)介紹了java在工作中中Hutool包的一些常用方法總結(jié),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-12-12
  • Java OpenSSL生成的RSA公私鑰進(jìn)行數(shù)據(jù)加解密詳細(xì)介紹

    Java OpenSSL生成的RSA公私鑰進(jìn)行數(shù)據(jù)加解密詳細(xì)介紹

    這篇文章主要介紹了Java OpenSSL生成的RSA公私鑰進(jìn)行數(shù)據(jù)加解密詳細(xì)介紹的相關(guān)資料,這里提供實(shí)例代碼及說(shuō)明具體如何實(shí)現(xiàn),需要的朋友可以參考下
    2016-12-12
  • springboot2.6.7集成springfox3.0.0的示例代碼

    springboot2.6.7集成springfox3.0.0的示例代碼

    這篇文章主要介紹了springboot2.6.7集成springfox3.0.0的示例代碼,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-04-04
  • Java集合去重導(dǎo)致的線(xiàn)上問(wèn)題

    Java集合去重導(dǎo)致的線(xiàn)上問(wèn)題

    這篇文章主要介紹了Java集合去重導(dǎo)致的線(xiàn)上問(wèn)題,根據(jù)主題提出Set是怎樣保證數(shù)據(jù)的唯一性的,為什么兩種去重方式性能差距這么大的問(wèn)題,下文解決思路需要的小伙伴可以參考一下
    2022-04-04

最新評(píng)論

贺州市| 九龙县| 富顺县| 铁岭县| 大名县| 肇东市| 湖北省| 梁河县| 定西市| 中牟县| 汉阴县| 白河县| 台北市| 丹阳市| 东宁县| 航空| 北川| 新昌县| 杭锦后旗| 来安县| 罗源县| 同江市| 阿荣旗| 泾川县| 自贡市| 淮安市| 科尔| 开远市| 安化县| 射洪县| 枣庄市| 浦东新区| 奈曼旗| 贵德县| 天水市| 明星| 和平区| 九龙坡区| 扶沟县| 封丘县| 拜泉县|