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

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

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

細(xì)數(shù)線程池的10個(gè)坑

jf_ro2CN3Fa ? 來(lái)源:撿田螺的小男孩 ? 2023-06-16 10:11 ? 次閱讀

前言

日常開(kāi)發(fā)中,為了更好管理線程資源,減少創(chuàng)建線程和銷(xiāo)毀線程的資源損耗,我們會(huì)使用線程池來(lái)執(zhí)行一些異步任務(wù)。但是線程池使用不當(dāng),就可能會(huì)引發(fā)生產(chǎn)事故。今天跟大家聊聊線程池的10個(gè)坑。大家看完肯定會(huì)有幫助的~

線程池默認(rèn)使用無(wú)界隊(duì)列,任務(wù)過(guò)多導(dǎo)致OOM

線程創(chuàng)建過(guò)多,導(dǎo)致OOM

共享線程池,次要邏輯拖垮主要邏輯

線程池拒絕策略的坑

Spring內(nèi)部線程池的坑

使用線程池時(shí),沒(méi)有自定義命名

線程池參數(shù)設(shè)置不合理

線程池異常處理的坑

使用完線程池忘記關(guān)閉

ThreadLocal與線程池搭配,線程復(fù)用,導(dǎo)致信息錯(cuò)亂。

1.線程池默認(rèn)使用無(wú)界隊(duì)列,任務(wù)過(guò)多導(dǎo)致OOM

JDK開(kāi)發(fā)者提供了線程池的實(shí)現(xiàn)類(lèi),我們基于Executors組件,就可以快速創(chuàng)建一個(gè)線程池 。日常工作中,一些小伙伴為了開(kāi)發(fā)效率,反手就用Executors新建個(gè)線程池。寫(xiě)出類(lèi)似以下的代碼:

publicclassNewFixedTest{

publicstaticvoidmain(String[]args){
ExecutorServiceexecutor=Executors.newFixedThreadPool(10);
for(inti=0;i{
try{
Thread.sleep(10000);
}catch(InterruptedExceptione){
//donothing
}
});
}
}
}

使用newFixedThreadPool創(chuàng)建的線程池,是會(huì)有坑的,它默認(rèn)是無(wú)界的阻塞隊(duì)列,如果任務(wù)過(guò)多,會(huì)導(dǎo)致OOM問(wèn)題。運(yùn)行一下以上代碼,出現(xiàn)了OOM。

Exceptioninthread"main"java.lang.OutOfMemoryError:GCoverheadlimitexceeded
atjava.util.concurrent.LinkedBlockingQueue.offer(LinkedBlockingQueue.java:416)
atjava.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1371)
atcom.example.dto.NewFixedTest.main(NewFixedTest.java:14)

這是因?yàn)閚ewFixedThreadPool使用了無(wú)界的阻塞隊(duì)列的LinkedBlockingQueue,如果線程獲取一個(gè)任務(wù)后,任務(wù)的執(zhí)行時(shí)間比較長(zhǎng)(比如,上面demo代碼設(shè)置了10秒),會(huì)導(dǎo)致隊(duì)列的任務(wù)越積越多,導(dǎo)致機(jī)器內(nèi)存使用不停飆升, 最終出現(xiàn)OOM。

看下newFixedThreadPool的相關(guān)源碼,是可以看到一個(gè)無(wú)界的阻塞隊(duì)列的,如下:

//阻塞隊(duì)列是LinkedBlockingQueue,并且是使用的是無(wú)參構(gòu)造函數(shù)
publicstaticExecutorServicenewFixedThreadPool(intnThreads){
returnnewThreadPoolExecutor(nThreads,nThreads,
0L,TimeUnit.MILLISECONDS,
newLinkedBlockingQueue());
}

//無(wú)參構(gòu)造函數(shù),默認(rèn)最大容量是Integer.MAX_VALUE,相當(dāng)于無(wú)界的阻塞隊(duì)列的了
publicLinkedBlockingQueue(){
this(Integer.MAX_VALUE);
}

因此,工作中,建議大家自定義線程池 ,并使用指定長(zhǎng)度的阻塞隊(duì)列 。

2. 線程池創(chuàng)建線程過(guò)多,導(dǎo)致OOM

有些小伙伴說(shuō),既然Executors組件創(chuàng)建出的線程池newFixedThreadPool,使用的是無(wú)界隊(duì)列,可能會(huì)導(dǎo)致OOM。那么,Executors組件還可以創(chuàng)建別的線程池,如newCachedThreadPool,我們用它也不行嘛?

我們可以看下newCachedThreadPool的構(gòu)造函數(shù):

publicstaticExecutorServicenewCachedThreadPool(){
returnnewThreadPoolExecutor(0,Integer.MAX_VALUE,
60L,TimeUnit.SECONDS,
newSynchronousQueue());
}

它的最大線程數(shù)是Integer.MAX_VALUE。大家應(yīng)該意識(shí)到使用它,可能會(huì)引發(fā)什么問(wèn)題了吧。沒(méi)錯(cuò),如果創(chuàng)建了大量的線程也有可能引發(fā)OOM!

筆者在以前公司,遇到這么一個(gè)OOM問(wèn)題:一個(gè)第三方提供的包,是直接使用new Thread實(shí)現(xiàn)多線程的。在某個(gè)夜深人靜的夜晚,我們的監(jiān)控系統(tǒng)報(bào)警了。。。這個(gè)相關(guān)的業(yè)務(wù)請(qǐng)求瞬間特別多,監(jiān)控系統(tǒng)告警OOM了。

所以我們使用線程池的時(shí)候,還要當(dāng)心線程創(chuàng)建過(guò)多,導(dǎo)致OOM問(wèn)題。大家盡量不要使用newCachedThreadPool,并且如果自定義線程池時(shí),要注意一下最大線程數(shù)。

3. 共享線程池,次要邏輯拖垮主要邏輯

要避免所有的業(yè)務(wù)邏輯共享一個(gè)線程池。比如你用線程池A來(lái)做登錄異步通知,又用線程池A來(lái)做對(duì)賬。如下圖:

28fb8944-0bea-11ee-962d-dac502259ad0.png

如果對(duì)賬任務(wù)checkBillService響應(yīng)時(shí)間過(guò)慢,會(huì)占據(jù)大量的線程池資源,可能直接導(dǎo)致沒(méi)有足夠的線程資源去執(zhí)行l(wèi)oginNotifyService的任務(wù),最后影響登錄。就這樣,因?yàn)橐粋€(gè)次要服務(wù),影響到重要的登錄接口,顯然這是絕對(duì)不允許的。因此,我們不能將所有的業(yè)務(wù)一鍋燉,都共享一個(gè)線程池,因?yàn)檫@樣做,風(fēng)險(xiǎn)太高了,猶如所有雞蛋放到一個(gè)籃子里。應(yīng)當(dāng)做線程池隔離

291bb8d6-0bea-11ee-962d-dac502259ad0.png

4. 線程池拒絕策略的坑,使用不當(dāng)導(dǎo)致阻塞

我們知道線程池主要有四種拒絕策略,如下:

AbortPolicy: 丟棄任務(wù)并拋出RejectedExecutionException異常。(默認(rèn)拒絕策略)

DiscardPolicy:丟棄任務(wù),但是不拋出異常。

DiscardOldestPolicy:丟棄隊(duì)列最前面的任務(wù),然后重新嘗試執(zhí)行任務(wù)。

CallerRunsPolicy:由調(diào)用方線程處理該任務(wù)。

如果線程池拒絕策略設(shè)置不合理,就容易有坑。我們把拒絕策略設(shè)置為DiscardPolicy或DiscardOldestPolicy并且在被拒絕的任務(wù),F(xiàn)uture對(duì)象調(diào)用get()方法,那么調(diào)用線程會(huì)一直被阻塞。

我們來(lái)看個(gè)demo:

publicclassDiscardThreadPoolTest{

publicstaticvoidmain(String[]args)throwsExecutionException,InterruptedException{
//一個(gè)核心線程,隊(duì)列最大為1,最大線程數(shù)也是1.拒絕策略是DiscardPolicy
ThreadPoolExecutorexecutorService=newThreadPoolExecutor(1,1,1L,TimeUnit.MINUTES,
newArrayBlockingQueue<>(1),newThreadPoolExecutor.DiscardPolicy());

Futuref1=executorService.submit(()->{
System.out.println("提交任務(wù)1");
try{
Thread.sleep(3000);
}catch(InterruptedExceptione){
e.printStackTrace();
}
});

Futuref2=executorService.submit(()->{
System.out.println("提交任務(wù)2");
});

Futuref3=executorService.submit(()->{
System.out.println("提交任務(wù)3");
});

System.out.println("任務(wù)1完成"+f1.get());//等待任務(wù)1執(zhí)行完畢
System.out.println("任務(wù)2完成"+f2.get());//等待任務(wù)2執(zhí)行完畢
System.out.println("任務(wù)3完成"+f3.get());//等待任務(wù)3執(zhí)行完畢

executorService.shutdown();//關(guān)閉線程池,阻塞直到所有任務(wù)執(zhí)行完畢

}
}

運(yùn)行結(jié)果:一直在運(yùn)行中。。。

2940e796-0bea-11ee-962d-dac502259ad0.png

這是因?yàn)镈iscardPolicy拒絕策略,是什么都沒(méi)做,源碼如下:

publicstaticclassDiscardPolicyimplementsRejectedExecutionHandler{
/**
*Createsa{@codeDiscardPolicy}.
*/
publicDiscardPolicy(){}

/**
*Doesnothing,whichhastheeffectofdiscardingtaskr.
*/
publicvoidrejectedExecution(Runnabler,ThreadPoolExecutore){
}
}

我們?cè)賮?lái)看看線程池 submit 的方法:

publicFuturesubmit(Runnabletask){
if(task==null)thrownewNullPointerException();
//把Runnable任務(wù)包裝為Future對(duì)象
RunnableFutureftask=newTaskFor(task,null);
//執(zhí)行任務(wù)
execute(ftask);
//返回Future對(duì)象
returnftask;
}

publicFutureTask(Runnablerunnable,Vresult){
this.callable=Executors.callable(runnable,result);
this.state=NEW;//Future的初始化狀態(tài)是New
}

我們?cè)賮?lái)看看Future的get() 方法

//狀態(tài)大于COMPLETING,才會(huì)返回,要不然都會(huì)阻塞等待
publicVget()throwsInterruptedException,ExecutionException{
ints=state;
if(s<=?COMPLETING)
????????????s?=?awaitDone(false,?0L);
????????return?report(s);
????}
????
????FutureTask的狀態(tài)枚舉
????private?static?final?int?NEW??????????=?0;
????private?static?final?int?COMPLETING???=?1;
????private?static?final?int?NORMAL???????=?2;
????private?static?final?int?EXCEPTIONAL??=?3;
????private?static?final?int?CANCELLED????=?4;
????private?static?final?int?INTERRUPTING?=?5;
????private?static?final?int?INTERRUPTED??=?6;

阻塞的真相水落石出啦,F(xiàn)utureTask的狀態(tài)大于COMPLETING才會(huì)返回,要不然都會(huì)一直阻塞等待 。又因?yàn)榫芙^策略啥沒(méi)做,沒(méi)有修改FutureTask的狀態(tài),因此FutureTask的狀態(tài)一直是NEW,所以它不會(huì)返回,會(huì)一直等待。

這個(gè)問(wèn)題,可以使用別的拒絕策略,比如CallerRunsPolicy,它讓主線程去執(zhí)行拒絕的任務(wù),會(huì)更新FutureTask狀態(tài)。如果確實(shí)想用DiscardPolicy,則需要重寫(xiě)DiscardPolicy的拒絕策略。

溫馨提示 ,日常開(kāi)發(fā)中,使用 Future.get() 時(shí),盡量使用帶超時(shí)時(shí)間的 ,因?yàn)樗亲枞摹?/p>

future.get(1,TimeUnit.SECONDS);

難道使用別的拒絕策略,就萬(wàn)無(wú)一失了嘛? 不是的,如果使用CallerRunsPolicy拒絕策略,它表示拒絕的任務(wù)給調(diào)用方線程用,如果這是主線程,那會(huì)不會(huì)可能也導(dǎo)致主線程阻塞 呢?總結(jié)起來(lái),大家日常開(kāi)發(fā)的時(shí)候,多一份心眼吧,多一點(diǎn)思考吧。

5. Spring內(nèi)部線程池的坑

工作中,個(gè)別開(kāi)發(fā)者,為了快速開(kāi)發(fā),喜歡直接用spring的@Async,來(lái)執(zhí)行異步任務(wù)。

@Async
publicvoidtestAsync()throwsInterruptedException{
System.out.println("處理異步任務(wù)");
TimeUnit.SECONDS.sleep(newRandom().nextInt(100));
}

Spring內(nèi)部線程池,其實(shí)是SimpleAsyncTaskExecutor,這玩意有點(diǎn)坑,它不會(huì)復(fù)用線程的 ,它的設(shè)計(jì)初衷就是執(zhí)行大量的短時(shí)間的任務(wù)。有興趣的小伙伴,可以去看看它的源碼:

/**
*{@linkTaskExecutor}implementationthatfiresupanewThreadforeachtask,
*executingitasynchronously.
*
*

Supportslimitingconcurrentthreadsthroughthe"concurrencyLimit" *beanproperty.Bydefault,thenumberofconcurrentthreadsisunlimited. * *

NOTE:Thisimplementationdoesnotreusethreads!Considera *thread-poolingTaskExecutorimplementationinstead,inparticularfor *executingalargenumberofshort-livedtasks. * *@authorJuergenHoeller *@since2.0 *@see#setConcurrencyLimit *@seeSyncTaskExecutor *@seeorg.springframework.scheduling.concurrent.ThreadPoolTaskExecutor *@seeorg.springframework.scheduling.commonj.WorkManagerTaskExecutor */ @SuppressWarnings("serial") publicclassSimpleAsyncTaskExecutorextendsCustomizableThreadCreatorimplementsAsyncListenableTaskExecutor,Serializable{ ...... }

也就是說(shuō)來(lái)了一個(gè)請(qǐng)求,就會(huì)新建一個(gè)線程!大家使用spring的@Async時(shí),要避開(kāi)這個(gè)坑,自己再定義一個(gè)線程池。正例如下:

@Bean(name="threadPoolTaskExecutor")
publicExecutorthreadPoolTaskExecutor(){
ThreadPoolTaskExecutorexecutor=newThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setThreadNamePrefix("tianluo-%d");
//其他參數(shù)設(shè)置
returnnewThreadPoolTaskExecutor();
}

6. 使用線程池時(shí),沒(méi)有自定義命名

使用線程池時(shí),如果沒(méi)有給線程池一個(gè)有意義的名稱(chēng),將不好排查回溯問(wèn)題。這不算一個(gè)坑吧,只能說(shuō)給以后排查埋坑 ,哈哈。我還是單獨(dú)把它放出來(lái)算一個(gè)點(diǎn),因?yàn)閭€(gè)人覺(jué)得這個(gè)還是比較重要的。反例如下:

publicclassThreadTest{

publicstaticvoidmain(String[]args)throwsException{
ThreadPoolExecutorexecutorOne=newThreadPoolExecutor(5,5,1,
TimeUnit.MINUTES,newArrayBlockingQueue(20));
executorOne.execute(()->{
System.out.println("關(guān)注:芋道源碼");
thrownewNullPointerException();
});
}
}

運(yùn)行結(jié)果:

Exceptioninthread"pool-1-thread-1"java.lang.NullPointerException
atcom.example.dto.ThreadTest.lambda$main$0(ThreadTest.java:17)
atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
atjava.lang.Thread.run(Thread.java:748)

可以發(fā)現(xiàn),默認(rèn)打印的線程池名字是pool-1-thread-1,如果排查問(wèn)題起來(lái),并不友好。因此建議大家給自己線程池自定義個(gè)容易識(shí)別的名字。其實(shí)用CustomizableThreadFactory即可,正例如下:

publicclassThreadTest{

publicstaticvoidmain(String[]args)throwsException{
ThreadPoolExecutorexecutorOne=newThreadPoolExecutor(5,5,1,
TimeUnit.MINUTES,newArrayBlockingQueue(20),newCustomizableThreadFactory("Tianluo-Thread-pool"));
executorOne.execute(()->{
System.out.println("關(guān)注:芋道源碼");
thrownewNullPointerException();
});
}
}

7. 線程池參數(shù)設(shè)置不合理

線程池最容易出坑的地方,就是線程參數(shù)設(shè)置不合理。比如核心線程設(shè)置多少合理,最大線程池設(shè)置多少合理等等。當(dāng)然,這塊不是亂設(shè)置的,需要結(jié)合具體業(yè)務(wù) 。

比如線程池如何調(diào)優(yōu),如何確認(rèn)最佳線程數(shù)?

最佳線程數(shù)目=((線程等待時(shí)間+線程CPU時(shí)間)/線程CPU時(shí)間)*CPU數(shù)目

我們的服務(wù)器CPU核數(shù)為8核,一個(gè)任務(wù)線程cpu耗時(shí)為20ms,線程等待(網(wǎng)絡(luò)IO、磁盤(pán)IO)耗時(shí)80ms,那最佳線程數(shù)目:( 80 + 20 )/20 * 8 = 40。也就是設(shè)置 40個(gè)線程數(shù)最佳。

8. 線程池異常處理的坑

我們來(lái)看段代碼:

publicclassThreadTest{

publicstaticvoidmain(String[]args)throwsException{
ThreadPoolExecutorexecutorOne=newThreadPoolExecutor(5,5,1,
TimeUnit.MINUTES,newArrayBlockingQueue(20),newCustomizableThreadFactory("Tianluo-Thread-pool"));
for(inti=0;i{
System.out.println("currentthreadname"+Thread.currentThread().getName());
Objectobject=null;
System.out.print("result#"+object.toString());
});
}

}
}

按道理,運(yùn)行這塊代碼應(yīng)該拋空指針異常 才是的,對(duì)吧。但是,運(yùn)行結(jié)果卻是這樣的;

currentthreadnameTianluo-Thread-pool1
currentthreadnameTianluo-Thread-pool2
currentthreadnameTianluo-Thread-pool3
currentthreadnameTianluo-Thread-pool4
currentthreadnameTianluo-Thread-pool5

這是因?yàn)槭褂胹ubmit提交任務(wù),不會(huì)把異常直接這樣拋出來(lái)。大家有興趣的話,可以去看看源碼。可以改為execute方法執(zhí)行,當(dāng)然最好就是try...catch捕獲,如下:

publicclassThreadTest{

publicstaticvoidmain(String[]args)throwsException{
ThreadPoolExecutorexecutorOne=newThreadPoolExecutor(5,5,1,
TimeUnit.MINUTES,newArrayBlockingQueue(20),newCustomizableThreadFactory("Tianluo-Thread-pool"));
for(inti=0;i{
System.out.println("currentthreadname"+Thread.currentThread().getName());
try{
Objectobject=null;
System.out.print("result#"+object.toString());
}catch(Exceptione){
System.out.println("異常了"+e);
}
});
}

}
}

其實(shí),我們還可以為工作者線程設(shè)置UncaughtExceptionHandler,在uncaughtException方法中處理異常。大家知道這個(gè)坑就好啦。

9. 線程池使用完畢后,忘記關(guān)閉

如果線程池使用完,忘記關(guān)閉的話,有可能會(huì)導(dǎo)致內(nèi)存泄露 問(wèn)題。所以,大家使用完線程池后,記得關(guān)閉一下。同時(shí),線程池最好也設(shè)計(jì)成單例模式,給它一個(gè)好的命名,以方便排查問(wèn)題。

publicclassThreadTest{

publicstaticvoidmain(String[]args)throwsException{

ThreadPoolExecutorexecutorOne=newThreadPoolExecutor(5,5,1,
TimeUnit.MINUTES,newArrayBlockingQueue(20),newCustomizableThreadFactory("Tianluo-Thread-pool"));
executorOne.execute(()->{
System.out.println("關(guān)注:芋道源碼");
});

//關(guān)閉線程池
executorOne.shutdown();
}
}

10. ThreadLocal與線程池搭配,線程復(fù)用,導(dǎo)致信息錯(cuò)亂。

使用ThreadLocal緩存信息,如果配合線程池一起,有可能出現(xiàn)信息錯(cuò)亂的情況。先看下一下例子:

privatestaticfinalThreadLocalcurrentUser=ThreadLocal.withInitial(()->null);

@GetMapping("wrong")
publicMapwrong(@RequestParam("userId")IntegeruserId){
//設(shè)置用戶(hù)信息之前先查詢(xún)一次ThreadLocal中的用戶(hù)信息
Stringbefore=Thread.currentThread().getName()+":"+currentUser.get();
//設(shè)置用戶(hù)信息到ThreadLocal
currentUser.set(userId);
//設(shè)置用戶(hù)信息之后再查詢(xún)一次ThreadLocal中的用戶(hù)信息
Stringafter=Thread.currentThread().getName()+":"+currentUser.get();
//匯總輸出兩次查詢(xún)結(jié)果
Mapresult=newHashMap();
result.put("before",before);
result.put("after",after);
returnresult;
}

按理說(shuō),每次獲取的before應(yīng)該都是null,但是呢,程序運(yùn)行在 Tomcat 中,執(zhí)行程序的線程是Tomcat的工作線程,而Tomcat的工作線程是基于線程池 的。

線程池會(huì)重用固定的幾個(gè)線程 ,一旦線程重用,那么很可能首次從 ThreadLocal 獲取的值是之前其他用戶(hù)的請(qǐng)求遺留的值。這時(shí),ThreadLocal 中的用戶(hù)信息就是其他用戶(hù)的信息。

把tomcat的工作線程設(shè)置為1

server.tomcat.max-threads=1

用戶(hù)1,請(qǐng)求過(guò)來(lái),會(huì)有以下結(jié)果,符合預(yù)期:

29627f00-0bea-11ee-962d-dac502259ad0.png

用戶(hù)2請(qǐng)求過(guò)來(lái),會(huì)有以下結(jié)果,「不符合預(yù)期」:

298af502-0bea-11ee-962d-dac502259ad0.png

因此,使用類(lèi)似 ThreadLocal 工具來(lái)存放一些數(shù)據(jù)時(shí),需要特別注意在代碼運(yùn)行完后,顯式地去清空設(shè)置的數(shù)據(jù),正例如下:

@GetMapping("right")
publicMapright(@RequestParam("userId")IntegeruserId){
Stringbefore=Thread.currentThread().getName()+":"+currentUser.get();
currentUser.set(userId);
try{
Stringafter=Thread.currentThread().getName()+":"+currentUser.get();
Mapresult=newHashMap();
result.put("before",before);
result.put("after",after);
returnresult;
}finally{
//在finally代碼塊中刪除ThreadLocal中的數(shù)據(jù),確保數(shù)據(jù)不串
currentUser.remove();
}
}




審核編輯:劉清

聲明:本文內(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)投訴
  • 線程池
    +關(guān)注

    關(guān)注

    0

    文章

    56

    瀏覽量

    6826
  • JDK
    JDK
    +關(guān)注

    關(guān)注

    0

    文章

    81

    瀏覽量

    16567
  • Thread
    +關(guān)注

    關(guān)注

    2

    文章

    83

    瀏覽量

    25893

原文標(biāo)題:細(xì)數(shù)線程池的10個(gè)坑,面試線程不怕不怕啦

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

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Java中的線程包括哪些

    java.util.concurrent 包來(lái)實(shí)現(xiàn)的,最主要的就是 ThreadPoolExecutor 類(lèi)。 Executor: 代表線程的接口,有一個(gè) execute() 方法,給一個(gè)
    的頭像 發(fā)表于 10-11 15:33 ?777次閱讀
    Java中的<b class='flag-5'>線程</b><b class='flag-5'>池</b>包括哪些

    線程是如何實(shí)現(xiàn)的

    線程的概念是什么?線程是如何實(shí)現(xiàn)的?
    發(fā)表于 02-28 06:20

    java自帶的線程方法

    二、原理分析 從上面使用線程的例子來(lái)看,最主要就是兩步,構(gòu)造ThreadPoolExecutor對(duì)象,然后每來(lái)一個(gè)任務(wù),就調(diào)用ThreadPoolExecutor對(duì)象的execute方法。 1
    發(fā)表于 09-27 11:06 ?0次下載

    如何正確關(guān)閉線程

    前言本章分為兩個(gè)議題 如何正確關(guān)閉線程 shutdown 和 shutdownNow 的區(qū)別 項(xiàng)目環(huán)境jdk 1.8 github 地址:https://github.com
    的頭像 發(fā)表于 09-29 14:41 ?9868次閱讀

    基于Nacos的簡(jiǎn)單動(dòng)態(tài)化線程實(shí)現(xiàn)

    本文以Nacos作為服務(wù)配置中心,以修改線程核心線程數(shù)、最大線程數(shù)為例,實(shí)現(xiàn)一個(gè)簡(jiǎn)單的動(dòng)態(tài)化線程
    發(fā)表于 01-06 14:14 ?830次閱讀

    線程線程

    線程通常用于服務(wù)器應(yīng)用程序。 每個(gè)傳入請(qǐng)求都將分配給線程池中的一個(gè)線程,因此可以異步處理請(qǐng)求,而不會(huì)占用主
    的頭像 發(fā)表于 02-28 09:53 ?741次閱讀
    多<b class='flag-5'>線程</b>之<b class='flag-5'>線程</b><b class='flag-5'>池</b>

    Java線程核心原理

    看過(guò)Java線程源碼的小伙伴都知道,在Java線程池中最核心的類(lèi)就是ThreadPoolExecutor,
    的頭像 發(fā)表于 04-21 10:24 ?816次閱讀

    線程線程怎么釋放

    線程分組看,pool名開(kāi)頭線程占616條,而且waiting狀態(tài)也是616條,這個(gè)點(diǎn)就非??梢闪?,我斷定就是這個(gè)pool開(kāi)頭線程導(dǎo)致的問(wèn)題。我們先排查為何這個(gè)
    發(fā)表于 07-31 10:49 ?2206次閱讀
    <b class='flag-5'>線程</b><b class='flag-5'>池</b>的<b class='flag-5'>線程</b>怎么釋放

    線程的兩個(gè)思考

    今天還是說(shuō)一下線程的兩個(gè)思考。 池子 我們常用的線程, JDK的ThreadPoolExecutor. CompletableFutur
    的頭像 發(fā)表于 09-30 11:21 ?3074次閱讀
    <b class='flag-5'>線程</b><b class='flag-5'>池</b>的兩<b class='flag-5'>個(gè)</b>思考

    Spring 的線程應(yīng)用

    我們?cè)谌粘i_(kāi)發(fā)中,經(jīng)常跟多線程打交道,Spring 為我們提供了一個(gè)線程方便我們開(kāi)發(fā),它就是 ThreadPoolTaskExecutor ,接下來(lái)我們就來(lái)聊聊 Spring 的
    的頭像 發(fā)表于 10-13 10:47 ?585次閱讀
    Spring 的<b class='flag-5'>線程</b><b class='flag-5'>池</b>應(yīng)用

    線程基本概念與原理

    、17、20等的新特性,簡(jiǎn)化了多線程編程的實(shí)現(xiàn)。 提高性能與資源利用率 線程主要解決兩個(gè)問(wèn)題:線程創(chuàng)建與銷(xiāo)毀的開(kāi)銷(xiāo)以及
    的頭像 發(fā)表于 11-10 10:24 ?465次閱讀

    線程的基本概念

    線程的基本概念 不管線程是什么東西!但是我們必須知道線程被搞出來(lái)的目的就是:提高程序執(zhí)行效
    的頭像 發(fā)表于 11-10 16:37 ?483次閱讀
    <b class='flag-5'>線程</b><b class='flag-5'>池</b>的基本概念

    線程七大核心參數(shù)執(zhí)行順序

    以及它們的執(zhí)行順序。 corePoolSize(核心線程數(shù)): 線程池中一直存活的線程數(shù)量。在線程初始化或者任務(wù)提交后,
    的頭像 發(fā)表于 12-04 16:45 ?912次閱讀

    線程的創(chuàng)建方式有幾種

    線程是一種用于管理和調(diào)度線程的技術(shù),能夠有效地提高系統(tǒng)的性能和資源利用率。它通過(guò)預(yù)先創(chuàng)建一組線程并維護(hù)一個(gè)工作隊(duì)列,將任務(wù)提交給
    的頭像 發(fā)表于 12-04 16:52 ?801次閱讀

    什么是動(dòng)態(tài)線程?動(dòng)態(tài)線程的簡(jiǎn)單實(shí)現(xiàn)思路

    因此,動(dòng)態(tài)可監(jiān)控線程一種針對(duì)以上痛點(diǎn)開(kāi)發(fā)的線程管理工具。主要可實(shí)現(xiàn)功能有:提供對(duì) Spring 應(yīng)用內(nèi)線程
    的頭像 發(fā)表于 02-28 10:42 ?567次閱讀