自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

事務(wù)中存在多線程,怎么處理?

開發(fā) 前端
Spring 管理的事務(wù)上下文是基于調(diào)用線程的,新線程并沒有繼承原線程的 TransactionSynchronizationManager 中的事務(wù)上下文。因此,新線程執(zhí)行的數(shù)據(jù)庫操作實際上是在無事務(wù)管理的環(huán)境下進(jìn)行的,這就導(dǎo)致事務(wù)失效。

在 Spring 框架中,@Transactional 注解作為一種聲明式事務(wù)管理的關(guān)鍵機(jī)制,其背后的工作原理遠(yuǎn)比簡單的 AOP(面向切面編程)和 ThreadLocal 存儲更為細(xì)膩。該注解的實現(xiàn)核心在于 Spring 的 TransactionInterceptor(事務(wù)攔截器)以及它如何與 Spring 的代理機(jī)制、TransactionManager(事務(wù)管理器)協(xié)同工作,來確保事務(wù)的開啟、提交或回滾等操作得以正確執(zhí)行。

一 注解解析與代理生成

當(dāng) Spring 容器初始化時,會通過 AnnotationTransactionAttributeSource 掃描并識別出所有標(biāo)有 @Transactional 的方法。這些方法在被調(diào)用前,Spring 會根據(jù)配置(如基于接口或類的代理)為它們創(chuàng)建動態(tài)代理對象。如果是基于接口的代理,則使用 JDK Dynamic Proxy;如果是基于類的,則采用 CGLIB。這個代理對象會在目標(biāo)方法調(diào)用前后插入事務(wù)處理邏輯。

1.1 Spring Bean的后處理器

一切始于 Spring 容器的 Bean 創(chuàng)建和初始化過程。Spring 通過一系列的 BeanPostProcessor 接口實現(xiàn)類來增強(qiáng) Bean 的功能,其中與事務(wù)管理密切相關(guān)的便是 AbstractAutoProxyCreator的子類,如 AnnotationAwareAspectJAutoProxyCreator。這個類負(fù)責(zé)掃描并創(chuàng)建代理對象,以便于在運行時織入諸如事務(wù)管理這樣的切面邏輯。

1.2 識別 @Transactional 注解

  • ClassPathScanningCandidateComponentProvider:Spring 首先會使用此類掃描指定包路徑下帶有特定注解(如@Transactional)的類或方法。
  • AnnotationTransactionAttributeSource:一旦找到帶有 @Transactional 注解的類或方法,Spring 會使用 AnnotationTransactionAttributeSource 來解析這些注解,將其轉(zhuǎn)換為事務(wù)屬性(TransactionAttribute),比如事務(wù)的隔離級別、傳播行為、超時時間等。

1.3 創(chuàng)建代理對象

  • 對于基于接口的代理,Spring 使用 JDK 的動態(tài)代理技術(shù),通過JdkDynamicAopProxy創(chuàng)建代理對象。該代理會檢查調(diào)用鏈,并在調(diào)用目標(biāo)方法前插入事務(wù)管理的前置邏輯,調(diào)用后插入后置邏輯(如提交或回滾事務(wù))。
  • 對于沒有實現(xiàn)接口的類,Spring 則利用 CGLIB 庫生成目標(biāo)類的子類作為代理,通過CglibAopProxy 來完成。CGLIB 代理同樣能夠在目標(biāo)方法調(diào)用的前后插入事務(wù)管理代碼。

1.4 TransactionInterceptor

  • 在代理對象中,事務(wù)管理的具體邏輯是由 TransactionInterceptor(事務(wù)攔截器)來執(zhí)行的。它實現(xiàn)了 MethodInterceptor 接口,因此當(dāng)代理對象的方法被調(diào)用時,會進(jìn)入invoke(MethodInvocation invocation) 方法。在這個方法內(nèi),TransactionInterceptor 根據(jù)解析出的事務(wù)屬性來決定是否開啟事務(wù)、使用何種傳播行為,并最終調(diào)用目標(biāo)方法。

總結(jié)下就是,Spring 通過 Bean 的后置處理器在容器初始化階段自動檢測帶有 @Transactional 注解的類和方法,并通過動態(tài)代理機(jī)制為這些組件創(chuàng)建代理對象。代理對象在方法調(diào)用時,通過 TransactionInterceptor 這一核心組件,根據(jù)注解配置的事務(wù)屬性來管理事務(wù)生命周期,確保事務(wù)邏輯的無縫集成。

二 事務(wù)攔截與執(zhí)行

2.1 TransactionInterceptor 的作用

TransactionInterceptor 實現(xiàn)了 Spring 的 MethodInterceptor 接口,這意味著它能夠攔截方法調(diào)用,并在調(diào)用前后執(zhí)行額外的邏輯,即事務(wù)管理邏輯。其核心職責(zé)包括:

  • 識別是否需要事務(wù): 根據(jù)目標(biāo)方法上的 @Transactional 注解(如果有的話)及其屬性(如傳播行為、隔離級別、超時時間等),決定是否需要啟動一個新的事務(wù)或加入到現(xiàn)有的事務(wù)中。需要注意的是,事務(wù)上下文(包括連接信息等)被存儲在由 Spring 管理的一個特定的 TransactionSynchronizationManager 中,TransactionSynchronizationManager 內(nèi)部使用了 ThreadLocal。
  • 事務(wù)管理: 在方法調(diào)用之前開啟事務(wù),調(diào)用之后根據(jù)方法執(zhí)行結(jié)果提交或回滾事務(wù)。這包括異常處理邏輯,即在遇到未被捕獲的異常時,確保事務(wù)被正確回滾。

2.2 攔截與執(zhí)行流程

  1. 方法調(diào)用前:
  • 事務(wù)屬性解析: TransactionInterceptor 首先通過TransactionAttributeSource(通常是AnnotationTransactionAttributeSource)獲取目標(biāo)方法的事務(wù)屬性。
  • 事務(wù)開始: 根據(jù)解析出的事務(wù)屬性,決定是否創(chuàng)建新的事務(wù)或者加入到當(dāng)前事務(wù)中。如果需要新事務(wù),它會通過事務(wù)管理器(如 PlatformTransactionManager 的實現(xiàn)類)來開始事務(wù)。
  1. 方法執(zhí)行:
  • 在事務(wù)上下文中執(zhí)行實際的目標(biāo)方法。此時,如果目標(biāo)方法內(nèi)部拋出了異常,這個異常會被暫存以供后續(xù)處理。

  1. 方法調(diào)用后:

  • 異常處理: 如果方法執(zhí)行過程中拋出了異常,TransactionInterceptor 會捕獲到這個異常,并根據(jù)異常類型及事務(wù)屬性決定是否需要回滾事務(wù)。通常,非檢查型異常(繼承自 RuntimeException 的異常)會導(dǎo)致事務(wù)回滾,而檢查型異常則不會,除非事務(wù)屬性特別指定了回滾規(guī)則。

  • 事務(wù)提交或回滾: 如果方法正常結(jié)束,或者按事務(wù)屬性應(yīng)該提交事務(wù)的情況下,事務(wù)管理器會提交事務(wù)。相反,如果需要回滾,則執(zhí)行回滾操作。

  1. 資源清理: 在事務(wù)結(jié)束后,確保所有資源被正確釋放,比如關(guān)閉數(shù)據(jù)庫連接等。

2.3 動態(tài)代理的作用

整個過程中,動態(tài)代理扮演著關(guān)鍵角色。無論是 JDK 動態(tài)代理還是 CGLIB 代理,它們都是在調(diào)用真正業(yè)務(wù)方法之前插入事務(wù)攔截邏輯的橋梁。當(dāng)客戶端代碼調(diào)用代理對象上的方法時,實際上是調(diào)用了由 TransactionInterceptor 所控制的代理邏輯,從而透明地在業(yè)務(wù)邏輯執(zhí)行前后管理事務(wù)。

通過這種方式,開發(fā)者無需在每個需要事務(wù)管理的方法中手動編寫開啟、提交或回滾事務(wù)的代碼,極大地簡化了開發(fā)復(fù)雜度,提高了代碼的可維護(hù)性和可讀性。

三 多線程環(huán)境下的挑戰(zhàn)

當(dāng) @Transactional 標(biāo)記的方法內(nèi)部啟動新的線程時,問題就顯現(xiàn)了。前面提到,事務(wù)攔截使用了 TransactionInterceptor,而 TransactionInterceptor 內(nèi)部用到了  TransactionSynchronizationManager,TransactionSynchronizationManager 使用 ThreadLocal 來存儲事務(wù)相關(guān)的資源信息,如數(shù)據(jù)庫連接、JMS 會話等。這意味著每個線程都有其獨立的事務(wù)上下文,確保了事務(wù)信息在線程間的隔離。

換句話說,Spring 管理的事務(wù)上下文是基于調(diào)用線程的,新線程并沒有繼承原線程的 TransactionSynchronizationManager 中的事務(wù)上下文。因此,新線程執(zhí)行的數(shù)據(jù)庫操作實際上是在無事務(wù)管理的環(huán)境下進(jìn)行的,這就導(dǎo)致事務(wù)失效。

那如果非要用多線程怎么辦呢?這個時候可以使用編程式事務(wù),首先設(shè)置一個全局變量 Boolean,默認(rèn)是可以提交的 true,在子線程,通過編程式事務(wù)的方式去開啟事務(wù),然后插入數(shù)據(jù),插入完成后,事務(wù)先不提交,同時通知主線程,我準(zhǔn)備好了,進(jìn)入等待狀態(tài)。如果子線程出現(xiàn)異常,那就通知主線程,我這邊發(fā)生異常,然后自己回滾。

最后主線程收集各個子線程的狀態(tài),如果有一個線程出現(xiàn)問題,那么全局變量就設(shè)置為不可提交false,然后喚醒所有子線程,進(jìn)行回滾。

這里涉及到線程同步可以利用閉鎖去實現(xiàn);當(dāng)主線程通知各個子線程提交事務(wù)的時候,子線程可能在提交的時候出錯了,最終導(dǎo)致數(shù)據(jù)不一致,那么這種時候可能就需要引入重試機(jī)制,有了重試機(jī)制后,又要去保證冪等性等等。

這一套方案下來大伙有沒有覺得眼熟,是不是就是分布式事務(wù)的處理思路了?

所以說,非要在事務(wù)中開啟多線程也沒問題,但是不建議這么做。

責(zé)任編輯:武曉燕 來源: 江南一點雨
相關(guān)推薦

2022-05-20 10:20:17

Spring事務(wù)MyBatis

2024-04-30 12:56:00

多線程.NET

2020-05-14 09:31:48

Python多處理多線程

2015-11-18 18:56:36

Java多線程處理

2024-09-26 10:51:51

2020-04-29 09:10:26

Python多線程多處理

2024-10-18 16:58:26

2010-04-14 09:20:26

.NET多線程

2021-06-29 07:47:23

多線程協(xié)作數(shù)據(jù)

2011-06-13 10:03:19

Qt 多線程 編程

2010-03-17 09:33:30

Java多線程方案

2009-03-12 10:52:43

Java線程多線程

2011-06-30 17:31:32

Qt 多線程 信號

2013-12-02 17:33:20

Linux進(jìn)程多線程

2023-05-12 14:14:00

Java線程中斷

2022-03-04 10:17:04

Redis數(shù)據(jù)

2010-03-16 14:20:57

Java多線程調(diào)試

2021-03-26 05:54:00

C#數(shù)據(jù)方法

2021-06-10 00:13:43

C#隊列數(shù)據(jù)

2011-06-24 11:12:39

Qt 多線程 線程
點贊
收藏

51CTO技術(shù)棧公眾號