1.不應用線程池的缺點
- 有些開發(fā)者圖省事,遇到需要多線程處理的地方,直接new Thread(...).start(),對于一般場景是沒問題的,但如果是在并發(fā)請求很高的情況下,就會有些隱患:
- 新建線程的開銷。線程雖然比進程要輕量許多,但對于JVM來說,新建一個線程的代價還是挺大的,決不同于新建一個對象
- 資源消耗量。沒有一個池來限制線程的數(shù)量,會導致線程的數(shù)量直接取決于應用的并發(fā)量,這樣有潛在的線程數(shù)據(jù)巨大的可能,那么資源消耗量將是巨大的
- 穩(wěn)定性。當線程數(shù)量超過系統(tǒng)資源所能承受的程度,穩(wěn)定性就會成問題
2.制定執(zhí)行策略
- 在每個需要多線程處理的地方,不管并發(fā)量有多大,需要考慮線程的執(zhí)行策略
- 任務以什么順序執(zhí)行
- 可以有多少個任務并發(fā)執(zhí)行
- 可以有多少個任務進入等待執(zhí)行隊列
- 系統(tǒng)過載的時候,應該放棄哪些任務?如何通知到應用程序?
- 一個任務的執(zhí)行前后應該做什么處理
3.線程池的類型
- 不管是通過Executors創(chuàng)建線程池,還是通過Spring來管理,都得清楚知道有哪幾種線程池:
- FixedThreadPool:定長線程池,提交任務時創(chuàng)建線程,直到池的最大容量,如果有線程非預期結束,會補充新線程
- CachedThreadPool:可變線程池,它猶如一個彈簧,如果沒有任務需求時,它回收空閑線程,如果需求增加,則按需增加線程,不對池的大小做限制
- SingleThreadExecutor:單線程。處理不過來的任務會進入FIFO隊列等待執(zhí)行
- SecheduledThreadPool:周期性線程池。支持執(zhí)行周期性線程任務
-
其實,這些不同類型的線程池都是通過構建一個ThreadPoolExecutor來完成的,所不同的是
corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory這么幾個參數(shù)。具體可以參見JDK DOC。
4.線程池飽和策略
- 由以上線程池類型可知,除了CachedThreadPool其他線程池都有飽和的可能,當飽和以后就需要相應的策略處理請求線程的任務,比如,達到上限時通過ThreadPoolExecutor.setRejectedExecutionHandler方法設置一個拒絕任務的策略,JDK提供了AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy幾種策略,具體差異可見JDK DOC
5.線程無依賴性
- 多線程任務設計上盡量使得各任務是獨立無依賴的,所謂依賴性可兩個方面:
- 線程之間的依賴性。如果線程有依賴可能會造成死鎖或饑餓
- 調(diào)用者與線程的依賴性。調(diào)用者得監(jiān)視線程的完成情況,影響可并發(fā)量
當然,在有些業(yè)務里確實需要一定的依賴性,比如調(diào)用者需要得到線程完成后結果,傳統(tǒng)的Thread是不便完成的,因為run方法無返回值,只能通過一些共享的變量來傳遞結果,但在Executor框架里可以通過Future和Callable實現(xiàn)需要有返回值的任務,當然線程的異步性導致需要有相應機制來保證調(diào)用者能等待任務完成。