java中的分布式事務(wù)解決方式
背景
分布式事務(wù),后端開(kāi)發(fā)中比較常見(jiàn)啦。因?yàn)樵诿嬖嚨臅r(shí)候,總是有interviewers讓我給他普及一下分布式事務(wù),雖然我會(huì)的也不多呀但是還是淺淺說(shuō)一說(shuō);
今天心血來(lái)潮,好好地總結(jié)一下分布式事務(wù),希望每一位后端工程師都能徹底理解分布式事務(wù)。
什么是分布式事務(wù)?
答:既然是分布式,首先必然是分布式系統(tǒng)中的一個(gè)概念啦。單體應(yīng)用沒(méi)這個(gè)東西,也不需要這個(gè)東西。本地事務(wù)就夠啦,Spring給我們提供的注解@Transactional, InnoDB引擎會(huì)為我們保證事務(wù)的ACID特性。但是分布式系統(tǒng)中,目前大多數(shù)互聯(lián)網(wǎng)公司都在用分布式系統(tǒng),微服務(wù)架構(gòu)等。所以,學(xué)好分布式事務(wù)太有必要。
廢話(huà)不多說(shuō),直接上原理。
總結(jié)來(lái)說(shuō),分布式事務(wù)涉及了多個(gè)獨(dú)立的數(shù)據(jù)源(數(shù)據(jù)庫(kù))或者參與者的事務(wù)操作,這些數(shù)據(jù)源分布在不同的計(jì)算機(jī)或網(wǎng)絡(luò)中;分布式事務(wù)確保在不同節(jié)點(diǎn)之間的多個(gè)操作要么全部成功,要么全部失敗。
分布式事務(wù)解決方案
2PC(XA協(xié)議)
兩階段提交協(xié)議,也叫XA協(xié)議。主要包含兩個(gè)階段,第一個(gè)階段是預(yù)備階段,第二個(gè)階段是提交階段。
2PC協(xié)議首先有分事務(wù)協(xié)調(diào)者角色和事務(wù)參與者。協(xié)調(diào)者是事先指定好的一個(gè)節(jié)點(diǎn)。參與者是一些涉及到數(shù)據(jù)庫(kù)操作的表,暫時(shí)可以這樣理解。這些多個(gè)參與者一般是分布在不同的節(jié)點(diǎn)上。
- 準(zhǔn)備階段。協(xié)調(diào)者向所有參與者發(fā)送事務(wù)準(zhǔn)備請(qǐng)求,參與者執(zhí)事務(wù)操作,并回復(fù)協(xié)調(diào)者準(zhǔn)備就緒的消息;如果多個(gè)參與者中有一個(gè)參與者未準(zhǔn)備就緒或者發(fā)生錯(cuò)誤,那么協(xié)調(diào)者會(huì)發(fā)送中止請(qǐng)求。只有所有參與者都回復(fù)準(zhǔn)備就緒,才會(huì)進(jìn)入第二階段。
- 提交階段。所有參與者都已經(jīng)準(zhǔn)備就緒,協(xié)調(diào)者分別發(fā)送提交的消息,參與者收到消息以后,執(zhí)行事務(wù)的提交操作,并向協(xié)調(diào)者回復(fù)提交完成。協(xié)調(diào)者收到了所有事務(wù)參與者提交完成的消息后,整個(gè)分布式事務(wù)才算提交完成。如果有一個(gè)參與者未能提交或者發(fā)生錯(cuò)誤,那么協(xié)調(diào)者會(huì)向所有參與者發(fā)送中止請(qǐng)求,進(jìn)行事務(wù)的回滾操作。
如何評(píng)價(jià)2PC?
1、2PC有單點(diǎn)故障的問(wèn)題。一旦事務(wù)協(xié)調(diào)者故障(因?yàn)槭鞘褂玫搅四硞€(gè)節(jié)點(diǎn)嘛),那么整個(gè)事務(wù)將無(wú)法繼續(xù)進(jìn)行,陷入故障。
2、數(shù)據(jù)不一致。如果協(xié)調(diào)者在發(fā)送提交信息時(shí),只有部分參與者收到了消息,并執(zhí)行了提交,此時(shí)網(wǎng)絡(luò)異常,就導(dǎo)致只有部分參與者執(zhí)行了事物的提交,另一部分則沒(méi)有提交,從而造成一個(gè)數(shù)據(jù)不一致性。
3、阻塞風(fēng)險(xiǎn)。如果準(zhǔn)備階段,有一個(gè)參與者無(wú)法響應(yīng)或者失敗,那么整個(gè)系統(tǒng)都會(huì)陷入阻塞狀態(tài),等待超時(shí)處理。
4、性能問(wèn)題。整個(gè)鏈路是串行的,響應(yīng)時(shí)間較長(zhǎng),不適合高并發(fā)的場(chǎng)景。
3PC
三階段提交又稱(chēng)3PC,相對(duì)于2PC來(lái)說(shuō)增加了CanCommit階段和超時(shí)機(jī)制。如果某段時(shí)間內(nèi)沒(méi)有收到協(xié)調(diào)者的commit請(qǐng)求,那么就會(huì)自動(dòng)進(jìn)行commit,解決了2PC單點(diǎn)故障的問(wèn)題。
但是性能問(wèn)題和數(shù)據(jù)不一致性問(wèn)題還是沒(méi)解決。3PC的步驟是這樣的:
- 1、詢(xún)問(wèn)節(jié)點(diǎn)。CanCommit, 首先詢(xún)問(wèn)參與者,是否有能力完成此次事務(wù)?如果都返回yes,則進(jìn)入第二階段有一個(gè)返回no或等待響應(yīng)超時(shí),則中斷事務(wù),并向所有參與者發(fā)送abort請(qǐng)求。
- 2、準(zhǔn)備階段;同2PC。需要注意的是,參與者收到消息后開(kāi)始執(zhí)行事務(wù)操作,會(huì)首先將Undo和Redo信息記錄到事務(wù)日志中。參與者執(zhí)行完事務(wù)操作后,向協(xié)調(diào)者反饋ACK, 表示已經(jīng)準(zhǔn)備好提交了。
- 3、提交階段。同2PC。
TCC
TCC ,Try, Confirm, Cancel; 其實(shí)是采用的補(bǔ)償機(jī)制,其核心思想是:針對(duì)每個(gè)操作,都要注冊(cè)一個(gè)與其對(duì)應(yīng)的確認(rèn)和補(bǔ)償(撤銷(xiāo))操作。
它分為三個(gè)階段:
- 1、Try, 對(duì)業(yè)務(wù)系統(tǒng)做檢測(cè)及資源預(yù)留;
- 2、Confirm主要對(duì)業(yè)務(wù)系統(tǒng)做確認(rèn)提交;try階段執(zhí)行成功,并開(kāi)始執(zhí)行confirm,默認(rèn)是不會(huì)出錯(cuò)的;只要Try成功,那么Confirm 一定會(huì)成功。
- 3、Cancle, 主要是在業(yè)務(wù)執(zhí)行錯(cuò)誤,需要回滾的狀態(tài)下執(zhí)行的業(yè)務(wù)取消,對(duì)預(yù)留資源釋放!
舉個(gè)例子,假入 Bob 要向 Smith 轉(zhuǎn)賬,思路大概是:我們有一個(gè)本地方法,里面依次調(diào)用
1、首先在 Try 階段,要先調(diào)用遠(yuǎn)程接口把 Smith 和 Bob 的錢(qián)給凍結(jié)起來(lái)。凍結(jié)可以理解為一種特殊的扣減,以放在后續(xù)轉(zhuǎn)賬的時(shí)候,金額不夠轉(zhuǎn)。并發(fā)訪(fǎng)問(wèn)的時(shí)候,凍結(jié)操作需要加分布式鎖,避免我在執(zhí)行凍結(jié)扣減的時(shí)候,此時(shí)的金額發(fā)生了變化,導(dǎo)致凍結(jié)失敗或者不一致性??蹨p以后,生成一條凍結(jié)交易記錄,表示該凍結(jié)操作成功。
2、在 Confirm 階段,執(zhí)行遠(yuǎn)程調(diào)用的轉(zhuǎn)賬的操作,轉(zhuǎn)賬成功;成功后查詢(xún)出凍結(jié)交易記錄,將凍結(jié)狀態(tài)變更為已解凍;
3、如果第2步執(zhí)行成功,那么轉(zhuǎn)賬成功,如果第二步執(zhí)行失敗,則調(diào)用遠(yuǎn)程凍結(jié)接口對(duì)應(yīng)的解凍接口,將數(shù)據(jù)恢復(fù)為凍結(jié)前的樣子。
Try部分完成業(yè)務(wù)的準(zhǔn)備工作,confirm部分完成業(yè)務(wù)的提交,cancel部分完成事務(wù)的回滾?;驹砣缦聢D所示:
如上圖所示,每個(gè)分支事務(wù)都需要實(shí)現(xiàn)Try,Confirm,Cancel接口。TCC事務(wù)開(kāi)始時(shí),業(yè)務(wù)應(yīng)用會(huì)向事務(wù)協(xié)調(diào)器注冊(cè)啟動(dòng)事務(wù)。之后業(yè)務(wù)應(yīng)用會(huì)調(diào)用所有服務(wù)的Try接口,完成一階段準(zhǔn)備。之后事務(wù)協(xié)調(diào)器會(huì)根據(jù)Try接口返回情況,決定調(diào)用Confirm接口或者Cancel接口。如果接口調(diào)用失敗,會(huì)進(jìn)行重試。
總結(jié)的說(shuō),所有分支的Try操作成功,會(huì)進(jìn)入到Confirm; 有一個(gè)分支未成功,已經(jīng)執(zhí)行的操作都要回滾。從而恢復(fù)到嘗試階段之前的狀態(tài),以確保數(shù)據(jù)的一致性。
TCC的優(yōu)點(diǎn):
- 1、降低了鎖的粒度,減少了并發(fā)沖突;從而提高了吞吐量;每個(gè)業(yè)務(wù)執(zhí)行操作都有相應(yīng)的補(bǔ)償操作,不需要人工干預(yù)進(jìn)行補(bǔ)償。
- 不足之處也很明顯,
- 2、對(duì)業(yè)務(wù)有一定的入侵;改造成本高,代碼冗長(zhǎng);
- 3、實(shí)現(xiàn)難度較大。需要按照網(wǎng)絡(luò)狀態(tài)、系統(tǒng)故障等不同的失敗原因?qū)崿F(xiàn)不同的回滾策略。為了滿(mǎn)足一致性的要求,confirm和cancel接口必須實(shí)現(xiàn)冪等。
本地消息表+MQ最終一致性事務(wù)
本地消息表的思想是將分布式事務(wù)拆分為本地事務(wù)來(lái)處理,通過(guò)引入一個(gè)額外的消息表,存儲(chǔ)消息的處理狀態(tài),從而實(shí)現(xiàn)消息可靠性和一致性。
如上圖中的訂單系統(tǒng)和庫(kù)存系統(tǒng)是兩個(gè)獨(dú)立的系統(tǒng),所以數(shù)據(jù)庫(kù)也是獨(dú)立的。如果我們想要保證下訂單以及訂單中商品庫(kù)存扣減的原子性,就必然要使用分布式事務(wù)。使用本地消息表解決分布式事務(wù)的思路是這樣。首先,訂單系統(tǒng)正常進(jìn)行訂單業(yè)務(wù)數(shù)據(jù)存DB,同時(shí)將這個(gè)消息實(shí)體記錄存入消息表內(nèi),消息實(shí)體至少要有消息的id以及消息的處理狀態(tài);然后向MQ發(fā)送這條消息。
訂單系統(tǒng)不斷往MQ中生產(chǎn)消息,庫(kù)存系統(tǒng)去消費(fèi)這個(gè)MQ中的消息,取到消息以后,執(zhí)行庫(kù)存相關(guān)業(yè)務(wù)操作,保存至DB,最終返回一個(gè)成功處理的響應(yīng)給MQ。訂單系統(tǒng)發(fā)完消息以后,也會(huì)去讀取該消息的響應(yīng)處理消息,讀到以后,如果是成功處理,那么就更新訂單庫(kù)中本地消息表中的該消息狀態(tài),更新為已完成。
整個(gè)簡(jiǎn)單的思路是這樣,需要注意的是,我們要保證下訂單和訂單消息存入消息表是一個(gè)原子性的,使用本地事務(wù)處理,要么都成功,要么都失敗。
第二,如果最終消息表中的消息都是已完成就沒(méi)什么問(wèn)題,但是有時(shí)候, 由于數(shù)據(jù)庫(kù)網(wǎng)絡(luò)等不穩(wěn)定因素導(dǎo)致消息的狀態(tài)還是未完成。這種問(wèn)題,我們必須要解決。
可以使用定時(shí)任務(wù),定時(shí)任務(wù)周期性去輪詢(xún)本地消息表中的消息,如果某消息是未完成,那么可以觸發(fā)重試操作,繼續(xù)往MQ中發(fā)送這條消息,交給庫(kù)存系統(tǒng)處理。
如果1步驟訂單消息在保存的時(shí)候失敗,那么直接觸發(fā)回滾即可。1和2是同時(shí)成功,同時(shí)回滾的。如果3失敗了,由于本地消息表中已經(jīng)存在消息體,這時(shí)只需要輪詢(xún)消息重新通過(guò)消息中間件發(fā)送一次。
第三,如果庫(kù)存系統(tǒng)中在業(yè)務(wù)處理上失敗了,此時(shí)再重試已無(wú)效,可以發(fā)消息給事務(wù)主動(dòng)方訂單系統(tǒng)回滾事務(wù)。
如果庫(kù)存系統(tǒng)已經(jīng)消費(fèi)了消息,訂單系統(tǒng)需要回滾事務(wù)的話(huà),需要發(fā)消息通知庫(kù)存進(jìn)行回滾事務(wù)。
本地消息表+MQ由于消息是異步發(fā)送,實(shí)際上是一種最終一致性方案,即滿(mǎn)足base理論,所以在一些要求實(shí)時(shí)性的業(yè)務(wù)場(chǎng)景就不那么適用的,適用于實(shí)時(shí)性不高,能接受最終一致性的場(chǎng)景。
優(yōu)缺點(diǎn)總結(jié)
優(yōu)點(diǎn)
1、簡(jiǎn)單可靠。通過(guò)本地消息表,將消息先存儲(chǔ)到本地?cái)?shù)據(jù)庫(kù)中,再進(jìn)行異步發(fā)送,可以保證消息的可靠性和一致性。
2、高可用性。相對(duì)XA協(xié)議,由于消息是異步發(fā)送的,所以即使其他分支事務(wù)出現(xiàn)了服務(wù)不可用或者故障,也不會(huì)影響當(dāng)前事務(wù)的提交,保證了系統(tǒng)的高可用。
3、提升性能:將消息發(fā)送過(guò)程異步化,減少了事務(wù)的等待時(shí)間,提升了系統(tǒng)的性能。
缺點(diǎn)
1、需要維護(hù)額外的消息表,增加了系統(tǒng)的復(fù)雜性。
2、依賴(lài)于數(shù)據(jù)庫(kù)。由于本地消息表依賴(lài)于數(shù)據(jù)庫(kù),如果數(shù)據(jù)庫(kù)出現(xiàn)故障或性能問(wèn)題,會(huì)對(duì)整個(gè)系統(tǒng)的可用性和性能產(chǎn)生影響。
3、業(yè)務(wù)耦合。本地消息表與業(yè)務(wù)耦合在一起,難于做成通用性,不可獨(dú)立伸縮。
4、無(wú)法保證實(shí)時(shí)性:由于消息發(fā)送是異步的,無(wú)法保證消息的實(shí)時(shí)性,存在一定的延遲。
最后,對(duì)于實(shí)時(shí)性要求較高、對(duì)數(shù)據(jù)一致性要求更嚴(yán)格的場(chǎng)景,可能需要考慮使用分布式事務(wù)管理框架或消息中間件等更復(fù)雜的方案。
Seata
Seata的設(shè)計(jì)思路是將一個(gè)分布式事務(wù)理解為一個(gè)全局事務(wù),全局事務(wù)下面掛著若干個(gè)分支事務(wù)。每個(gè)分支事務(wù)又相當(dāng)于是本地事務(wù),滿(mǎn)足ACID的特性,因此我們操作分布式事務(wù)就像操作本地事務(wù)一樣。Seata 內(nèi)部定義了 3個(gè)模塊來(lái)處理全局事務(wù)和分支事務(wù)的關(guān)系和處理過(guò)程,這三個(gè)組件分別是:
- Transaction Coordinator (TC): 事務(wù)協(xié)調(diào)器,維護(hù)全局事務(wù)的運(yùn)行狀態(tài),負(fù)責(zé)協(xié)調(diào)并驅(qū)動(dòng)全局事務(wù)的提交或回滾。
- Transaction Manager (TM): 控制全局事務(wù)的邊界,負(fù)責(zé)開(kāi)啟一個(gè)全局事務(wù),并最終發(fā)起全局提交或全局回滾的決議。
- Resource Manager (RM): 控制分支事務(wù),負(fù)責(zé)分支注冊(cè)、狀態(tài)匯報(bào),并接收事務(wù)協(xié)調(diào)器的指令,驅(qū)動(dòng)分支(本地)事務(wù)的提交和回滾。
通過(guò)這三個(gè)模塊,完成全局事務(wù)的執(zhí)行流程,執(zhí)行步驟如下:
首先,TM向TC申請(qǐng)一個(gè)全局事務(wù),TC創(chuàng)建一個(gè)全局事務(wù),并返回一個(gè)XID,XID是全局事務(wù)的唯一標(biāo)識(shí)。然后,RM向TC注冊(cè)分支事務(wù),該分支歸屬于該XID的全局事務(wù)。RM注冊(cè)本地事務(wù),會(huì)完成一系列的本地事務(wù)操作,用于全局事務(wù)的提交或者回滾。
最后,TM將全局事務(wù)提交或者回滾的決策發(fā)送TC,TC調(diào)度 XID 全局事務(wù)下的分支事務(wù)完成提交或者回滾。
一般的,我們都使用Seate的AT模式,其實(shí)也是分為兩個(gè)階段,類(lèi)似于XA協(xié)議的方式。第一階段是準(zhǔn)備階段,第二階段是分布式事務(wù)的提交或者回滾。
第一階段
分支事務(wù)主要利用RM模塊中的JDBC代理,在業(yè)務(wù)數(shù)據(jù)提交時(shí),自動(dòng)攔截業(yè)務(wù)SQL,把業(yè)務(wù)數(shù)據(jù)在更新前后的數(shù)據(jù)鏡像組織成回滾日志,進(jìn)而生成undoLog; 然后利用本地事務(wù)的特性,將業(yè)務(wù)SQL和undolog寫(xiě)入同一個(gè)事務(wù)中,一起提交到數(shù)據(jù)庫(kù)中,保證業(yè)務(wù)SQL必定存在回滾日志,最后對(duì)分支事務(wù)狀態(tài)向TC進(jìn)行上報(bào)。
第二階段
1、TM決議全局事務(wù)提交。首先通知TC全局事務(wù)提交,說(shuō)明各個(gè)RM分支事務(wù)已經(jīng)完成了第一階段;此時(shí),TC會(huì)異步調(diào)度各個(gè)RM分支事務(wù)刪除對(duì)應(yīng)的undolog日志。這個(gè)過(guò)程是異步的,所以速度也比較快。
2、TM決議全局事務(wù)回滾。同樣,首先通知TC全局事務(wù)回滾,RM會(huì)收到TC發(fā)送的回滾請(qǐng)求,RM會(huì)通過(guò)XID找到對(duì)應(yīng)的undolog日志,利用本地事務(wù) ACID 特性,執(zhí)行回滾日志完成回滾操作,并刪除 undo log 日志,最后向TC進(jìn)行回滾結(jié)果上報(bào)。
業(yè)務(wù)對(duì)以上所有的流程都無(wú)感知,業(yè)務(wù)完全不關(guān)心全局事務(wù)的具體提交和回滾,而且最重要的一點(diǎn)是 Seata 將兩段式提交的同步協(xié)調(diào)分解到各個(gè)分支事務(wù)中了,分支事務(wù)與普通的本地事務(wù)無(wú)任何差異,這意味著我們使用 Seata 后,分布式事務(wù)就像使用本地事務(wù)一樣。
然后想說(shuō)一下,將數(shù)據(jù)庫(kù)層的事務(wù)協(xié)調(diào)機(jī)制交給了中間件層 Seata , Seata為什么是個(gè)中間層,這是因?yàn)椋琗A協(xié)議依賴(lài)的是數(shù)據(jù)庫(kù)層面來(lái)保障事務(wù)的一致性,也即是說(shuō) XA 的各個(gè)分支事務(wù)是在數(shù)據(jù)庫(kù)層面上驅(qū)動(dòng)的。而Seata在數(shù)據(jù)庫(kù)做了一層代理層,所以我們使用 Seata 時(shí),使用的數(shù)據(jù)源實(shí)際上用的是Seata自帶的數(shù)據(jù)源代理 DataSourceProxy。這個(gè)代理層具體體現(xiàn)在RM模塊。
其主要作用是解析 SQL,把業(yè)務(wù)數(shù)據(jù)在更新前后的數(shù)據(jù)鏡像組織成回滾日志,并將 undo log 日志插入 undo log 表中,保證每條更新數(shù)據(jù)的業(yè)務(wù) sql 都有對(duì)應(yīng)的回滾日志存在。這樣做的好處是:本地事務(wù)執(zhí)行完以后,可以立即釋放資源,然后向 TC上報(bào)分支狀態(tài)。TM決議全局提交時(shí),也不需要同步協(xié)調(diào)處理。
總接的來(lái)說(shuō),Seate有如下優(yōu)點(diǎn):
- 1、較高的性能表現(xiàn)。它極大減少了分支事務(wù)對(duì)資源的鎖定時(shí)間,完美避免了 XA 協(xié)議需要同步協(xié)調(diào)導(dǎo)致資源鎖定時(shí)間過(guò)長(zhǎng)的問(wèn)題。
- 2、易集成。 Seata 提供了與各種主流框架和中間件的集成支持,方便在現(xiàn)有系統(tǒng)中集成和使用。
- 3、支持多種存儲(chǔ)后端: Seata 支持多種存儲(chǔ)后端,可以根據(jù)實(shí)際需求選擇合適的存儲(chǔ)方式。
- 4、靈活配置: Seata 提供了豐富的配置選項(xiàng),可以根據(jù)需求進(jìn)行靈活配置,滿(mǎn)足不同的分布式事務(wù)需求。
但是因?yàn)樵谑褂脮r(shí)需要部署Seata服務(wù)端,集成Seata客戶(hù)端,所以也存在一些缺點(diǎn),比如:
- 部署和維護(hù)成本: 部署和維護(hù)分布式事務(wù)解決方案需要一定的成本和精力,特別是在大規(guī)模系統(tǒng)中。
- 依賴(lài)性: 引入 Seata 可能會(huì)增加系統(tǒng)的依賴(lài)性,需要謹(jǐn)慎評(píng)估是否真正需要使用分布式事務(wù)解決方案。
- 配置復(fù)雜性: 配置 Seata 可能需要一定的復(fù)雜性,特別是針對(duì)復(fù)雜的分布式系統(tǒng)和場(chǎng)景,需要仔細(xì)配置各種參數(shù)。
所以,可按需看業(yè)務(wù)情況是否真的需要使用Seate實(shí)現(xiàn)分布式事務(wù)。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Sa-Token記住我模式實(shí)現(xiàn)七天免登錄
這篇文章主要為大家介紹了Sa-Token記住我模式實(shí)現(xiàn)七天免登錄示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07Java?web開(kāi)發(fā)環(huán)境的搭建超完整步驟
這篇文章主要介紹了如何安裝和配置IDEA?2020.1.1?X64版本軟件,包括創(chuàng)建Java?Web項(xiàng)目、配置Tomcat、部署Tomcat?API以及創(chuàng)建和配置Servlet,通過(guò)這些步驟,新手可以快速搭建起Javaweb開(kāi)發(fā)環(huán)境,需要的朋友可以參考下2024-11-11java8 多個(gè)list對(duì)象用lambda求差集操作
這篇文章主要介紹了java8 多個(gè)list對(duì)象用lambda求差集操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09Feign如何解決服務(wù)之間調(diào)用傳遞token
這篇文章主要介紹了Feign如何解決服務(wù)之間調(diào)用傳遞token,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03springboot(thymeleaf)中th:field和th:value的區(qū)別及說(shuō)明
這篇文章主要介紹了springboot(thymeleaf)中th:field和th:value的區(qū)別及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10在Java中實(shí)現(xiàn)線(xiàn)程安全的單例模式的常見(jiàn)方式
單例模式是一種常用的軟件設(shè)計(jì)模式,它確保一個(gè)類(lèi)只有一個(gè)實(shí)例,并提供一個(gè)全局訪(fǎng)問(wèn)點(diǎn),在多線(xiàn)程環(huán)境下,確保單例模式的線(xiàn)程安全性是非常重要的,因?yàn)槎鄠€(gè)線(xiàn)程可能會(huì)同時(shí)嘗試創(chuàng)建實(shí)例,導(dǎo)致實(shí)例不唯一的問(wèn)題,本文介紹了在Java中實(shí)現(xiàn)線(xiàn)程安全的單例模式有幾種常見(jiàn)的方式2024-09-09Simple Java Mail郵件發(fā)送實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了Simple Java Mail郵件發(fā)送實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11