chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

代碼改成多線程調(diào)用之后帶來(lái)的9大問(wèn)題

jf_ro2CN3Fa ? 來(lái)源:蘇三說(shuō)技術(shù) ? 2023-04-17 10:19 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

前言

很多時(shí)候,我們?yōu)榱颂嵘?a target="_blank">接口的性能,會(huì)把之前單線程同步執(zhí)行的代碼,改成多線程異步執(zhí)行。

比如:查詢(xún)用戶(hù)信息接口,需要返回用戶(hù)基本信息、積分信息、成長(zhǎng)值信息,而用戶(hù)、積分和成長(zhǎng)值,需要調(diào)用不同的接口獲取數(shù)據(jù)。

如果查詢(xún)用戶(hù)信息接口,同步調(diào)用三個(gè)接口獲取數(shù)據(jù),會(huì)非常耗時(shí)。

這就非常有必要把三個(gè)接口調(diào)用,改成異步調(diào)用,最后匯總結(jié)果。

再比如:注冊(cè)用戶(hù)接口,該接口主要包含:寫(xiě)用戶(hù)表,分配權(quán)限,配置用戶(hù)導(dǎo)航頁(yè),發(fā)通知消息等功能。

該用戶(hù)注冊(cè)接口包含的業(yè)務(wù)邏輯比較多,如果在接口中同步執(zhí)行這些代碼,該接口響應(yīng)時(shí)間會(huì)非常慢。

這時(shí)就需要把業(yè)務(wù)邏輯梳理一下,劃分:核心邏輯和非核心邏輯。這個(gè)例子中的核心邏輯是:寫(xiě)用戶(hù)表和分配權(quán)限,非核心邏輯是:配置用戶(hù)導(dǎo)航頁(yè)和發(fā)通知消息。

顯然核心邏輯必須在接口中同步執(zhí)行,而非核心邏輯可以多線程異步執(zhí)行。

等等。

需要使用多線程的業(yè)務(wù)場(chǎng)景太多了,使用多線程異步執(zhí)行的好處不言而喻。

但我要說(shuō)的是,如果多線程沒(méi)有使用好,它也會(huì)給我們帶來(lái)很多意想不到的問(wèn)題,不信往后繼續(xù)看。

今天跟大家一起聊聊,代碼改成多線程調(diào)用之后,帶來(lái)的9大問(wèn)題。

1.獲取不到返回值

如果你通過(guò)直接繼承Thread類(lèi),或者實(shí)現(xiàn)Runnable接口的方式去創(chuàng)建線程。

那么,恭喜你,你將沒(méi)法獲取該線程方法的返回值。

使用線程的場(chǎng)景有兩種:

不需要關(guān)注線程方法的返回值。

需要關(guān)注線程方法的返回值。

大部分業(yè)務(wù)場(chǎng)景是不需要關(guān)注線程方法返回值的,但如果我們有些業(yè)務(wù)需要關(guān)注線程方法的返回值該怎么處理呢?

查詢(xún)用戶(hù)信息接口,需要返回用戶(hù)基本信息、積分信息、成長(zhǎng)值信息,而用戶(hù)、積分和成長(zhǎng)值,需要調(diào)用不同的接口獲取數(shù)據(jù)。

如下圖所示:

d4001c3c-dcc0-11ed-bfe3-dac502259ad0.png

Java8之前可以通過(guò)實(shí)現(xiàn)Callable接口,獲取線程返回結(jié)果。

Java8以后通過(guò)CompleteFuture類(lèi)實(shí)現(xiàn)該功能。我們這里以CompleteFuture為例:

publicUserInfogetUserInfo(Longid)throwsInterruptedException,ExecutionException{
finalUserInfouserInfo=newUserInfo();
CompletableFutureuserFuture=CompletableFuture.supplyAsync(()->{
getRemoteUserAndFill(id,userInfo);
returnBoolean.TRUE;
},executor);

CompletableFuturebonusFuture=CompletableFuture.supplyAsync(()->{
getRemoteBonusAndFill(id,userInfo);
returnBoolean.TRUE;
},executor);

CompletableFuturegrowthFuture=CompletableFuture.supplyAsync(()->{
getRemoteGrowthAndFill(id,userInfo);
returnBoolean.TRUE;
},executor);
CompletableFuture.allOf(userFuture,bonusFuture,growthFuture).join();

userFuture.get();
bonusFuture.get();
growthFuture.get();

returnuserInfo;
}

溫馨提醒一下,這兩種方式別忘了使用線程池。示例中我用到了executor,表示自定義的線程池,為了防止高并發(fā)場(chǎng)景下,出現(xiàn)線程過(guò)多的問(wèn)題。

此外,F(xiàn)ork/join框架也提供了執(zhí)行任務(wù)并返回結(jié)果的能力。

2.數(shù)據(jù)丟失

我們還是以注冊(cè)用戶(hù)接口為例,該接口主要包含:寫(xiě)用戶(hù)表,分配權(quán)限,配置用戶(hù)導(dǎo)航頁(yè),發(fā)通知消息等功能。

其中:寫(xiě)用戶(hù)表和分配權(quán)限功能,需要在一個(gè)事務(wù)中同步執(zhí)行。而剩余的配置用戶(hù)導(dǎo)航頁(yè)和發(fā)通知消息功能,使用多線程異步執(zhí)行。

表面上看起來(lái)沒(méi)問(wèn)題。

但如果前面的寫(xiě)用戶(hù)表和分配權(quán)限功能成功了,用戶(hù)注冊(cè)接口就直接返回成功了。

但如果后面異步執(zhí)行的配置用戶(hù)導(dǎo)航頁(yè),或發(fā)通知消息功能失敗了,怎么辦?

如下圖所示:

d4064a9e-dcc0-11ed-bfe3-dac502259ad0.png

該接口前面明明已經(jīng)提示用戶(hù)成功了,但結(jié)果后面又有一部分功能在多線程異步執(zhí)行中失敗了。

這時(shí)該如何處理呢?

沒(méi)錯(cuò),你可以做失敗重試。

但如果重試了一定的次數(shù),還是沒(méi)有成功,這條請(qǐng)求數(shù)據(jù)該如何處理呢?如果不做任何處理,該數(shù)據(jù)是不是就丟掉了?

為了防止數(shù)據(jù)丟失,可以用如下方案:

使用mq異步處理。在分配權(quán)限之后,發(fā)送一條mq消息,到mq服務(wù)器,然后在mq的消費(fèi)者中使用多線程,去配置用戶(hù)導(dǎo)航頁(yè)和發(fā)通知消息。如果mq消費(fèi)者中處理失敗了,可以自己重試。

使用job異步處理。在分配權(quán)限之后,往任務(wù)表中寫(xiě)一條數(shù)據(jù)。然后有個(gè)job定時(shí)掃描該表,然后配置用戶(hù)導(dǎo)航頁(yè)和發(fā)通知消息。如果job處理某條數(shù)據(jù)失敗了,可以在表中記錄一個(gè)重試次數(shù),然后不斷重試。但該方案有個(gè)缺點(diǎn),就是實(shí)時(shí)性可能不太高。

3.順序問(wèn)題

如果你使用了多線程,就必須接受一個(gè)非?,F(xiàn)實(shí)的問(wèn)題,即順序問(wèn)題。

假如之前代碼的執(zhí)行順序是:a,b,c,改成多線程執(zhí)行之后,代碼的執(zhí)行順序可能變成了:a,c,b。(這個(gè)跟cpu調(diào)度算法有關(guān))

例如:

publicstaticvoidmain(String[]args){
Threadthread1=newThread(()->System.out.println("a"));
Threadthread2=newThread(()->System.out.println("b"));
Threadthread3=newThread(()->System.out.println("c"));

thread1.start();
thread2.start();
thread3.start();
}

執(zhí)行結(jié)果:

a
c
b

那么,來(lái)自靈魂的一問(wèn):如何保證線程的順序呢?

即線程啟動(dòng)的順序是:a,b,c,執(zhí)行的順序也是:a,b,c。

如下圖所示:

d413f73e-dcc0-11ed-bfe3-dac502259ad0.png

3.1 join

Thread類(lèi)的join方法它會(huì)讓主線程等待子線程運(yùn)行結(jié)束后,才能繼續(xù)運(yùn)行。

列如:

publicstaticvoidmain(String[]args)throwsInterruptedException{
Threadthread1=newThread(()->System.out.println("a"));
Threadthread2=newThread(()->System.out.println("b"));
Threadthread3=newThread(()->System.out.println("c"));

thread1.start();
thread1.join();
thread2.start();
thread2.join();
thread3.start();
}

執(zhí)行結(jié)果永遠(yuǎn)都是:

a
b
c

3.2 newSingleThreadExecutor

我們可以使用JDK自帶的Excutors類(lèi)的newSingleThreadExecutor方法,創(chuàng)建一個(gè)單線程的線程池。

例如:

publicstaticvoidmain(String[]args){
ExecutorServiceexecutorService=Executors.newSingleThreadExecutor();

Threadthread1=newThread(()->System.out.println("a"));
Threadthread2=newThread(()->System.out.println("b"));
Threadthread3=newThread(()->System.out.println("c"));

executorService.submit(thread1);
executorService.submit(thread2);
executorService.submit(thread3);

executorService.shutdown();
}

執(zhí)行結(jié)果永遠(yuǎn)都是:

a
b
c

使用Excutors類(lèi)的newSingleThreadExecutor方法創(chuàng)建的單線程的線程池,使用了LinkedBlockingQueue作為隊(duì)列,而此隊(duì)列按 FIFO(先進(jìn)先出)排序元素。

添加到隊(duì)列的順序是a,b,c,則執(zhí)行的順序也是a,b,c。

3.3 CountDownLatch

CountDownLatch是一個(gè)同步工具類(lèi),它允許一個(gè)或多個(gè)線程一直等待,直到其他線程執(zhí)行完后再執(zhí)行。

例如:

publicclassThreadTest{

publicstaticvoidmain(String[]args)throwsInterruptedException{
CountDownLatchlatch1=newCountDownLatch(0);
CountDownLatchlatch2=newCountDownLatch(1);
CountDownLatchlatch3=newCountDownLatch(1);

Threadthread1=newThread(newTestRunnable(latch1,latch2,"a"));
Threadthread2=newThread(newTestRunnable(latch2,latch3,"b"));
Threadthread3=newThread(newTestRunnable(latch3,latch3,"c"));

thread1.start();
thread2.start();
thread3.start();
}
}

classTestRunnableimplementsRunnable{

privateCountDownLatchlatch1;
privateCountDownLatchlatch2;
privateStringmessage;

TestRunnable(CountDownLatchlatch1,CountDownLatchlatch2,Stringmessage){
this.latch1=latch1;
this.latch2=latch2;
this.message=message;
}

@Override
publicvoidrun(){
try{
latch1.await();
System.out.println(message);
}catch(InterruptedExceptione){
e.printStackTrace();
}
latch2.countDown();
}
}

執(zhí)行結(jié)果永遠(yuǎn)都是:

a
b
c

此外,使用CompletableFuture的thenRun方法,也能多線程的執(zhí)行順序,在這里就不一一介紹了。

4.線程安全問(wèn)題

既然使用了線程,伴隨而來(lái)的還會(huì)有線程安全問(wèn)題。

假如現(xiàn)在有這樣一個(gè)需求:用多線程執(zhí)行查詢(xún)方法,然后把執(zhí)行結(jié)果添加到一個(gè)list集合中。

代碼如下:

Listlist=Lists.newArrayList();
dataList.stream()
.map(data->CompletableFuture
.supplyAsync(()->query(list,data),asyncExecutor)
));
CompletableFuture.allOf(futureArray).join();

使用CompletableFuture異步多線程執(zhí)行query方法:

publicvoidquery(Listlist,UserEntitycondition){
Useruser=queryByCondition(condition);
if(Objects.isNull(user)){
return;
}
list.add(user);
UserExtenduserExtend=queryByOther(condition);
if(Objects.nonNull(userExtend)){
user.setExtend(userExtend.getInfo());
}
}

在query方法中,將獲取的查詢(xún)結(jié)果添加到list集合中。

結(jié)果list會(huì)出現(xiàn)線程安全問(wèn)題,有時(shí)候會(huì)少數(shù)據(jù),當(dāng)然也不一定是必現(xiàn)的。

這是因?yàn)锳rrayList是非線程安全的,沒(méi)有使用synchronized等關(guān)鍵字修飾。

如何解決這個(gè)問(wèn)題呢?

答:使用CopyOnWriteArrayList集合,代替普通的ArrayList集合,CopyOnWriteArrayList是一個(gè)線程安全的機(jī)會(huì)。

只需一行小小的改動(dòng)即可:

ListlistLists.newCopyOnWriteArrayList();

溫馨的提醒一下,這里創(chuàng)建集合的方式,用了google的collect包。

5.ThreadLocal獲取數(shù)據(jù)異常

我們都知道JDK為了解決線程安全問(wèn)題,提供了一種用空間換時(shí)間的新思路:ThreadLocal。

它的核心思想是:共享變量在每個(gè)線程都有一個(gè)副本,每個(gè)線程操作的都是自己的副本,對(duì)另外的線程沒(méi)有影響。

例如:

@Service
publicclassThreadLocalService{
privatestaticfinalThreadLocalthreadLocal=newThreadLocal<>();

publicvoidadd(){
threadLocal.set(1);
doSamething();
Integerinteger=threadLocal.get();
}
}

ThreadLocal在普通中線程中,的確能夠獲取正確的數(shù)據(jù)。

但在真實(shí)的業(yè)務(wù)場(chǎng)景中,一般很少用單獨(dú)的線程,絕大多數(shù),都是用的線程池。

那么,在線程池中如何獲取ThreadLocal對(duì)象生成的數(shù)據(jù)呢?

如果直接使用普通ThreadLocal,顯然是獲取不到正確數(shù)據(jù)的。

我們先試試InheritableThreadLocal,具體代碼如下:

privatestaticvoidfun1(){
InheritableThreadLocalthreadLocal=newInheritableThreadLocal<>();
threadLocal.set(6);
System.out.println("父線程獲取數(shù)據(jù):"+threadLocal.get());

ExecutorServiceexecutorService=Executors.newSingleThreadExecutor();

threadLocal.set(6);
executorService.submit(()->{
System.out.println("第一次從線程池中獲取數(shù)據(jù):"+threadLocal.get());
});

threadLocal.set(7);
executorService.submit(()->{
System.out.println("第二次從線程池中獲取數(shù)據(jù):"+threadLocal.get());
});
}

執(zhí)行結(jié)果:

父線程獲取數(shù)據(jù):6
第一次從線程池中獲取數(shù)據(jù):6
第二次從線程池中獲取數(shù)據(jù):6

由于這個(gè)例子中使用了單例線程池,固定線程數(shù)是1。

第一次submit任務(wù)的時(shí)候,該線程池會(huì)自動(dòng)創(chuàng)建一個(gè)線程。因?yàn)槭褂昧薎nheritableThreadLocal,所以創(chuàng)建線程時(shí),會(huì)調(diào)用它的init方法,將父線程中的inheritableThreadLocals數(shù)據(jù)復(fù)制到子線程中。所以我們看到,在主線程中將數(shù)據(jù)設(shè)置成6,第一次從線程池中獲取了正確的數(shù)據(jù)6。

之后,在主線程中又將數(shù)據(jù)改成7,但在第二次從線程池中獲取數(shù)據(jù)卻依然是6。

因?yàn)榈诙蝧ubmit任務(wù)的時(shí)候,線程池中已經(jīng)有一個(gè)線程了,就直接拿過(guò)來(lái)復(fù)用,不會(huì)再重新創(chuàng)建線程了。所以不會(huì)再調(diào)用線程的init方法,所以第二次其實(shí)沒(méi)有獲取到最新的數(shù)據(jù)7,還是獲取的老數(shù)據(jù)6。

那么,這該怎么辦呢?

答:使用TransmittableThreadLocal,它并非JDK自帶的類(lèi),而是阿里巴巴開(kāi)源jar包中的類(lèi)。

可以通過(guò)如下pom文件引入該jar包:


com.alibaba
transmittable-thread-local
2.11.0
compile

代碼調(diào)整如下:

privatestaticvoidfun2()throwsException{
TransmittableThreadLocalthreadLocal=newTransmittableThreadLocal<>();
threadLocal.set(6);
System.out.println("父線程獲取數(shù)據(jù):"+threadLocal.get());

ExecutorServicettlExecutorService=TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(1));

threadLocal.set(6);
ttlExecutorService.submit(()->{
System.out.println("第一次從線程池中獲取數(shù)據(jù):"+threadLocal.get());
});

threadLocal.set(7);
ttlExecutorService.submit(()->{
System.out.println("第二次從線程池中獲取數(shù)據(jù):"+threadLocal.get());
});

}

執(zhí)行結(jié)果:

父線程獲取數(shù)據(jù):6
第一次從線程池中獲取數(shù)據(jù):6
第二次從線程池中獲取數(shù)據(jù):7

我們看到,使用了TransmittableThreadLocal之后,第二次從線程中也能正確獲取最新的數(shù)據(jù)7了。

nice。

如果你仔細(xì)觀察這個(gè)例子,你可能會(huì)發(fā)現(xiàn),代碼中除了使用TransmittableThreadLocal類(lèi)之外,還使用了TtlExecutors.getTtlExecutorService方法,去創(chuàng)建ExecutorService對(duì)象。

這是非常重要的地方,如果沒(méi)有這一步,TransmittableThreadLocal在線程池中共享數(shù)據(jù)將不會(huì)起作用。

創(chuàng)建ExecutorService對(duì)象,底層的submit方法會(huì)TtlRunnable或TtlCallable對(duì)象。

以TtlRunnable類(lèi)為例,它實(shí)現(xiàn)了Runnable接口,同時(shí)還實(shí)現(xiàn)了它的run方法:

publicvoidrun(){
Map,Object>copied=(Map)this.copiedRef.get();
if(copied!=null&&(!this.releaseTtlValueReferenceAfterRun||this.copiedRef.compareAndSet(copied,(Object)null))){
Mapbackup=TransmittableThreadLocal.backupAndSetToCopied(copied);

try{
this.runnable.run();
}finally{
TransmittableThreadLocal.restoreBackup(backup);
}
}else{
thrownewIllegalStateException("TTLvaluereferenceisreleasedafterrun!");
}
}

這段代碼的主要邏輯如下:

把當(dāng)時(shí)的ThreadLocal做個(gè)備份,然后將父類(lèi)的ThreadLocal拷貝過(guò)來(lái)。

執(zhí)行真正的run方法,可以獲取到父類(lèi)最新的ThreadLocal數(shù)據(jù)。

從備份的數(shù)據(jù)中,恢復(fù)當(dāng)時(shí)的ThreadLocal數(shù)據(jù)。

6.OOM問(wèn)題

眾所周知,使用多線程可以提升代碼執(zhí)行效率,但也不是絕對(duì)的。

對(duì)于一些耗時(shí)的操作,使用多線程,確實(shí)可以提升代碼執(zhí)行效率。

但線程不是創(chuàng)建越多越好,如果線程創(chuàng)建多了,也可能會(huì)導(dǎo)致OOM異常。

例如:

Causedby:
java.lang.OutOfMemoryError:unabletocreatenewnativethread

在JVM中創(chuàng)建一個(gè)線程,默認(rèn)需要占用1M的內(nèi)存空間。

如果創(chuàng)建了過(guò)多的線程,必然會(huì)導(dǎo)致內(nèi)存空間不足,從而出現(xiàn)OOM異常。

除此之外,如果使用線程池的話(huà),特別是使用固定大小線程池,即使用Executors.newFixedThreadPool方法創(chuàng)建的線程池。

該線程池的核心線程數(shù)和最大線程數(shù)是一樣的,是一個(gè)固定值,而存放消息的隊(duì)列是LinkedBlockingQueue。

該隊(duì)列的最大容量是Integer.MAX_VALUE,也就是說(shuō)如果使用固定大小線程池,存放了太多的任務(wù),有可能也會(huì)導(dǎo)致OOM異常。

java.lang.OutOfMemeryError:Javaheapspace

7.CPU使用率飆高

不知道你有沒(méi)有做過(guò)excel數(shù)據(jù)導(dǎo)入功能,需要將一批excel的數(shù)據(jù)導(dǎo)入到系統(tǒng)中。

每條數(shù)據(jù)都有些業(yè)務(wù)邏輯,如果單線程導(dǎo)入所有的數(shù)據(jù),導(dǎo)入效率會(huì)非常低。

于是改成了多線程導(dǎo)入。

如果excel中有大量的數(shù)據(jù),很可能會(huì)出現(xiàn)CPU使用率飆高的問(wèn)題。

我們都知道,如果代碼出現(xiàn)死循環(huán),cpu使用率會(huì)飚的很多高。因?yàn)榇a一直在某個(gè)線程中循環(huán),沒(méi)法切換到其他線程,cpu一直被占用著,所以會(huì)導(dǎo)致cpu使用率一直高居不下。

而多線程導(dǎo)入大量的數(shù)據(jù),雖說(shuō)沒(méi)有死循環(huán)代碼,但由于多個(gè)線程一直在不停的處理數(shù)據(jù),導(dǎo)致占用了cpu很長(zhǎng)的時(shí)間。

也會(huì)出現(xiàn)cpu使用率很高的問(wèn)題。

那么,如何解決這個(gè)問(wèn)題呢?

答:使用Thread.sleep休眠一下。

在線程中處理完一條數(shù)據(jù),休眠10毫秒。

當(dāng)然CPU使用率飆高的原因很多,多線程處理數(shù)據(jù)和死循環(huán)只是其中兩種,還有比如:頻繁GC、正則匹配、頻繁序列化和反序列化等。

后面我會(huì)寫(xiě)一篇介紹CPU使用率飆高的原因的專(zhuān)題文章,感興趣的小伙伴,可以關(guān)注一下我后續(xù)的文章。

8.事務(wù)問(wèn)題

在實(shí)際項(xiàng)目開(kāi)發(fā)中,多線程的使用場(chǎng)景還是挺多的。如果spring事務(wù)用在多線程場(chǎng)景中,會(huì)有問(wèn)題嗎?

例如:

@Slf4j
@Service
publicclassUserService{

@Autowired
privateUserMapperuserMapper;
@Autowired
privateRoleServiceroleService;

@Transactional
publicvoidadd(UserModeluserModel)throwsException{
userMapper.insertUser(userModel);
newThread(()->{
roleService.doOtherThing();
}).start();
}
}

@Service
publicclassRoleService{

@Transactional
publicvoiddoOtherThing(){
System.out.println("保存role表數(shù)據(jù)");
}
}

從上面的例子中,我們可以看到事務(wù)方法add中,調(diào)用了事務(wù)方法doOtherThing,但是事務(wù)方法doOtherThing是在另外一個(gè)線程中調(diào)用的。

這樣會(huì)導(dǎo)致兩個(gè)方法不在同一個(gè)線程中,獲取到的數(shù)據(jù)庫(kù)連接不一樣,從而是兩個(gè)不同的事務(wù)。如果想doOtherThing方法中拋了異常,add方法也回滾是不可能的。

如果看過(guò)spring事務(wù)源碼的朋友,可能會(huì)知道spring的事務(wù)是通過(guò)數(shù)據(jù)庫(kù)連接來(lái)實(shí)現(xiàn)的。當(dāng)前線程中保存了一個(gè)map,key是數(shù)據(jù)源,value是數(shù)據(jù)庫(kù)連接。

privatestaticfinalThreadLocal>resources=

newNamedThreadLocal<>("Transactionalresources");

我們說(shuō)的同一個(gè)事務(wù),其實(shí)是指同一個(gè)數(shù)據(jù)庫(kù)連接,只有擁有同一個(gè)數(shù)據(jù)庫(kù)連接才能同時(shí)提交和回滾。如果在不同的線程,拿到的數(shù)據(jù)庫(kù)連接肯定是不一樣的,所以是不同的事務(wù)。

所以不要在事務(wù)中開(kāi)啟另外的線程,去處理業(yè)務(wù)邏輯,這樣會(huì)導(dǎo)致事務(wù)失效。

9.導(dǎo)致服務(wù)掛掉

使用多線程會(huì)導(dǎo)致服務(wù)掛掉,這不是危言聳聽(tīng),而是確有其事。

假設(shè)現(xiàn)在有這樣一種業(yè)務(wù)場(chǎng)景:在mq的消費(fèi)者中需要調(diào)用訂單查詢(xún)接口,查到數(shù)據(jù)之后,寫(xiě)入業(yè)務(wù)表中。

本來(lái)是沒(méi)啥問(wèn)題的。

突然有一天,mq生產(chǎn)者跑了一個(gè)批量數(shù)據(jù)處理的job,導(dǎo)致mq服務(wù)器上堆積了大量的消息。

此時(shí),mq消費(fèi)者的處理速度,遠(yuǎn)遠(yuǎn)跟不上mq消息的生產(chǎn)速度,導(dǎo)致的結(jié)果是出現(xiàn)了大量的消息堆積,對(duì)用戶(hù)有很大的影響。

為了解決這個(gè)問(wèn)題,mq消費(fèi)者改成多線程處理,直接使用了線程池,并且最大線程數(shù)配置成了20。

這樣調(diào)整之后,消息堆積問(wèn)題確實(shí)得到了解決。

但帶來(lái)了另外一個(gè)更嚴(yán)重的問(wèn)題:訂單查詢(xún)接口并發(fā)量太大了,有點(diǎn)扛不住壓力,導(dǎo)致部分節(jié)點(diǎn)的服務(wù)直接掛掉。

d41cac9e-dcc0-11ed-bfe3-dac502259ad0.png

為了解決問(wèn)題,不得不臨時(shí)加服務(wù)節(jié)點(diǎn)。

在mq的消費(fèi)者中使用多線程,調(diào)用接口時(shí),一定要評(píng)估好接口能夠承受的最大訪問(wèn)量,防止因?yàn)閴毫^(guò)大,而導(dǎo)致服務(wù)掛掉的問(wèn)題。





審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    11213

    瀏覽量

    222775
  • 服務(wù)器
    +關(guān)注

    關(guān)注

    13

    文章

    10077

    瀏覽量

    90827
  • JAVA
    +關(guān)注

    關(guān)注

    20

    文章

    2997

    瀏覽量

    115612

原文標(biāo)題:麻了,代碼改成多線程,竟有9大問(wèn)題

文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    Linux多線程對(duì)比單線程的優(yōu)勢(shì)

    :「資源利用率」:通過(guò)多線程,可以更有效地利用CPU資源,特別是多核CPU?!覆⑿刑幚怼梗?b class='flag-5'>線程允許同時(shí)執(zhí)行多個(gè)任務(wù),提高程序的執(zhí)行效率?!负?jiǎn)化設(shè)計(jì)」:使用線程可以簡(jiǎn)化程序設(shè)計(jì),因?yàn)?b class='flag-5'>線程
    發(fā)表于 12-01 06:11

    rt-thread studio 如何進(jìn)行多線程編譯?

    ,使用的是5800h+32g內(nèi)存+sn550 ssd,開(kāi)啟16線程編譯時(shí)cpu的占用率也只能到30%,編譯完整個(gè)工程需要3分鐘 感覺(jué)多線程編譯設(shè)置沒(méi)有生效,有辦法提高編譯速度嗎 rtthread studio版本是 2.2.9
    發(fā)表于 10-11 09:16

    切換線程后中斷被屏蔽怎么解決?

    退出,已經(jīng)切換到main線程,但是發(fā)現(xiàn)此時(shí)中斷是被屏蔽的。 跟代碼發(fā)現(xiàn)rt_schedule最后切換線程時(shí)是先調(diào)用rt_hw_context_switch,再
    發(fā)表于 09-29 07:48

    【上海晶珩睿莓1開(kāi)發(fā)板試用體驗(yàn)】將TensorFlow-Lite物體歸類(lèi)(classify)的輸出圖片移植到LVGL9.3界面中

    LVGL9.3是C代碼,如果一個(gè)工程中同時(shí)存在C++代碼和C代碼的話(huà),必須分開(kāi)編譯,這是基本要求,gcc編譯C代碼,g++編譯C++代碼。C
    發(fā)表于 09-21 00:39

    main線程創(chuàng)建中的rt_memset導(dǎo)致hardfault是為什么?

    main線程的棧大小設(shè)置成2048的時(shí)候rt_memset導(dǎo)致hardfault,改成512就不會(huì),這是為什么
    發(fā)表于 09-18 06:24

    線程調(diào)用rt_thread_mdelay()函數(shù)卡死的原因?怎么解決?

    線程調(diào)用rt_thread_mdelay()函數(shù)程序卡死。搞了兩天也不知道問(wèn)題出在哪,怎么解決。 int main(void) { interrupt_config
    發(fā)表于 09-11 08:11

    【HZ-T536開(kāi)發(fā)板免費(fèi)體驗(yàn)】—— linux創(chuàng)建線程

    自己的私有資源。 在linux系統(tǒng)中,線程狀態(tài)通常反映了當(dāng)前線程的當(dāng)前活動(dòng)和執(zhí)行階段。 主要分為: 1。運(yùn)行轉(zhuǎn)態(tài) 2。阻塞轉(zhuǎn)態(tài) 3。終止?fàn)顟B(tài) 如何區(qū)分單線程多線程? 在單個(gè)程序中只
    發(fā)表于 09-01 21:31

    【RA4E2開(kāi)發(fā)板評(píng)測(cè)】LED1及LED2輪流點(diǎn)亮并同時(shí)亮8秒,體驗(yàn)FreeRTOS多線程

    線程 -- 在頁(yè)面下方的屬性里Support Dynamic Allocation 改為 Enabled。見(jiàn)下圖。 保存配置文件,點(diǎn)擊生成項(xiàng)目代碼。 4 編寫(xiě)多線程FreeRTOS代碼
    發(fā)表于 08-24 17:24

    從底層解讀labview的TDMS高級(jí)異步寫(xiě)入的工作原理

    文件。物理寫(xiě)入操作本身在某個(gè)瞬間通常只有一個(gè)在進(jìn)行。 線程安全由庫(kù)保證: TDMS 庫(kù)內(nèi)部機(jī)制確保了多線程調(diào)用異步寫(xiě)入時(shí)文件的完整性和數(shù)據(jù)順序(按提交順序)。 簡(jiǎn)單來(lái)說(shuō): 想象一個(gè)餐廳。顧客(你
    發(fā)表于 08-14 17:05

    多線程的安全注意事項(xiàng)

    多線程安全是指多個(gè)線程同時(shí)訪問(wèn)或修改共享資源時(shí),能夠保證程序的正確性和可靠性。 開(kāi)發(fā)者選擇TaskPool或Worker進(jìn)行多線程開(kāi)發(fā)時(shí),在TaskPool和Worker的工作線程中導(dǎo)
    發(fā)表于 06-20 07:49

    鴻蒙5開(kāi)發(fā)寶藏案例分享---跨線程性能優(yōu)化指南

    ;>Worker</span>做多線程開(kāi)發(fā)時(shí),總遇到對(duì)象跨線程卡頓的問(wèn)題,原來(lái)鴻蒙早就提供了解決方案。下面結(jié)合代碼和實(shí)戰(zhàn)案例,帶你徹底玩轉(zhuǎn)性能優(yōu)化! 一、痛點(diǎn):跨線程
    發(fā)表于 06-12 17:13

    工控一體機(jī)多線程任務(wù)調(diào)度優(yōu)化:聚徽分享破解工業(yè)復(fù)雜流程高效協(xié)同密碼

    在當(dāng)今工業(yè) 4.0 的浪潮下,工業(yè)生產(chǎn)正朝著高度自動(dòng)化、智能化的方向大步邁進(jìn)。生產(chǎn)流程日益復(fù)雜,眾多任務(wù)需要同時(shí)、高效地協(xié)同執(zhí)行,這對(duì)工業(yè)控制系統(tǒng)的核心 —— 工控一體機(jī)提出了前所未有的挑戰(zhàn)。多線程
    的頭像 發(fā)表于 05-28 14:06 ?456次閱讀

    一種實(shí)時(shí)多線程VSLAM框架vS-Graphs介紹

    針對(duì)現(xiàn)有VSLAM系統(tǒng)語(yǔ)義表達(dá)不足、地圖可解釋性差的問(wèn)題,本文提出vS-Graphs,一種實(shí)時(shí)多線程VSLAM框架。該方案顯著提升了重建地圖的語(yǔ)義豐富度、可解釋性及定位精度。實(shí)驗(yàn)表明
    的頭像 發(fā)表于 04-19 14:07 ?711次閱讀
    一種實(shí)時(shí)<b class='flag-5'>多線程</b>VSLAM框架vS-Graphs介紹

    請(qǐng)問(wèn)如何在Python中實(shí)現(xiàn)多線程與多進(jìn)程的協(xié)作?

    大家好!我最近在開(kāi)發(fā)一個(gè)Python項(xiàng)目時(shí),需要同時(shí)處理多個(gè)任務(wù),且每個(gè)任務(wù)需要不同的計(jì)算資源。我想通過(guò)多線程和多進(jìn)程的組合來(lái)實(shí)現(xiàn)并發(fā),但遇到了一些問(wèn)題。 具體來(lái)說(shuō),我有兩個(gè)任務(wù),一個(gè)是I/O密集型
    發(fā)表于 03-11 06:57

    請(qǐng)問(wèn)rt-thread studio如何進(jìn)行多線程編譯?

    ,使用的是5800h+32g內(nèi)存+sn550 ssd,開(kāi)啟16線程編譯時(shí)cpu的占用率也只能到30%,編譯完整個(gè)工程需要3分鐘 感覺(jué)多線程編譯設(shè)置沒(méi)有生效,有辦法提高編譯速度嗎
    發(fā)表于 02-19 08:30