springboot tomcat默认线程数_记一次JAVA线程池的错误用法
最近項目一個項目要結(jié)項了,但客戶要求 TPS 能達(dá)到上千,而用我寫的代碼再怎么弄成只能達(dá)到 30 + 的 TPS,然后我又將代碼中能緩存的都緩存了,能拆分的也都拆分了,拆分時用的線程池來實現(xiàn)的;其實現(xiàn)的代碼主要為以前寫的一篇博客中的實現(xiàn)方式來實現(xiàn)的。如下:
多線程之 futureTask(future,callable) 實例, jdbc 數(shù)據(jù)多線程查詢https://blog.csdn.net/puhaiyang/article/details/78041046
在其中用到了線程池,為了方便,線程池是采用如下代碼 new 出來的
final?ExecutorService?executorService?=?Executors.newFixedThreadPool(10);??通過自己仔細(xì)想想后,感覺這代碼總有哪里寫得不對,為此特意寫了下 DEMO 代碼來,并用 jmeter 自己跑一下自己測下:
??@RequestMapping(value?=?"doTest")????public?Object?doTest(@RequestParam(defaultValue?=?"false")?Boolean?shutdown,
?????????????????????????@RequestParam(defaultValue?=?"10")?Integer?threadCount,
?????????????????????????@RequestParam(defaultValue?=?"100")?Integer?sleepTime,
?????????????????????????@RequestParam(defaultValue?=?"10")?Integer?queryCount)?{
????????long?beginTime?=?System.currentTimeMillis();
????????final?ExecutorService?executorService?=?Executors.newFixedThreadPool(threadCount);
????????for?(int?i?=?0;?i?????????????int?finalI?=?i;
????????????Callable?callable?=?new?Callable()?{
????????????????@Override
????????????????public?Integer?call()?throws?Exception?{
????????????????????Thread.sleep(sleepTime);
????????????????????logger.debug("index:{}?threadInfo:{}",?finalI,?Thread.currentThread().toString());return?finalI;
????????????????}
????????????};
????????????FutureTask?futureTask?=?new?FutureTask(callable);
????????????executorService.submit(futureTask);
????????}if?(shutdown)?{
????????????executorService.shutdown();
????????}
????????Long?endTime?=?System.currentTimeMillis();
????????endTime?=?endTime?-?beginTime;
????????logger.info("info:{}",?endTime);return?atomicInteger.addAndGet(endTime.intValue())?+?"???this:"?+?endTime;
????}?
代碼如上所示,然后我用 jmeter 對此進(jìn)行了測試,測試 1000 個請求去訪問,每個任務(wù)線程休眠時間設(shè)的為 1 秒,TPS 為 20 多。
一想這確實挺低的,然后分析其原因,想著是不是 springBoot 的線程數(shù)給的太少了,于是乎又把 tomcat 的最大線程數(shù)進(jìn)行了修改,由默認(rèn)的 200 修改為了 500,但發(fā)現(xiàn)沒啥大的變化,想了想后,可能問題不是 tomcat 的配置導(dǎo)致的。
server:??tomcat:
????max-threads:?500?
然后又通過 Java VisualVM 工具看了看線程信息,沒發(fā)現(xiàn)啥問題。
然后出去靜了靜,聽了一兩首音樂后想著起來 Executors 還有一個 newCachedThreadPool() 的用法,它與 newFixedThreadPool() 的區(qū)別通過源碼可以大概知道個一二:
newFixedThreadPool:
????/**?????*?Creates?a?thread?pool?that?reuses?a?fixed?number?of?threads
?????*?operating?off?a?shared?unbounded?queue.??At?any?point,?at?most
?????*?{@code?nThreads}?threads?will?be?active?processing?tasks.
?????*?If?additional?tasks?are?submitted?when?all?threads?are?active,
?????*?they?will?wait?in?the?queue?until?a?thread?is?available.
?????*?If?any?thread?terminates?due?to?a?failure?during?execution
?????*?prior?to?shutdown,?a?new?one?will?take?its?place?if?needed?to
?????*?execute?subsequent?tasks.??The?threads?in?the?pool?will?exist
?????*?until?it?is?explicitly?{@link?ExecutorService#shutdown?shutdown}.
?????*
?????*?@param?nThreads?the?number?of?threads?in?the?pool
?????*?@return?the?newly?created?thread?pool
?????*?@throws?IllegalArgumentException?if?{@code?nThreads?<=?0}
?????*/
????public?static?ExecutorService?newFixedThreadPool(int?nThreads)?{
????????return?new?ThreadPoolExecutor(nThreads,?nThreads,
??????????????????????????????????????0L,?TimeUnit.MILLISECONDS,
??????????????????????????????????????new?LinkedBlockingQueue());
????}
newCachedThreadPool:
????/**?????*?Creates?a?thread?pool?that?creates?new?threads?as?needed,?but
?????*?will?reuse?previously?constructed?threads?when?they?are
?????*?available.??These?pools?will?typically?improve?the?performance
?????*?of?programs?that?execute?many?short-lived?asynchronous?tasks.
?????*?Calls?to?{@code?execute}?will?reuse?previously?constructed
?????*?threads?if?available.?If?no?existing?thread?is?available,?a?new
?????*?thread?will?be?created?and?added?to?the?pool.?Threads?that?have
?????*?not?been?used?for?sixty?seconds?are?terminated?and?removed?from
?????*?the?cache.?Thus,?a?pool?that?remains?idle?for?long?enough?will
?????*?not?consume?any?resources.?Note?that?pools?with?similar
?????*?properties?but?different?details?(for?example,?timeout?parameters)
?????*?may?be?created?using?{@link?ThreadPoolExecutor}?constructors.
?????*
?????*?@return?the?newly?created?thread?pool
?????*/
????public?static?ExecutorService?newCachedThreadPool()?{
????????return?new?ThreadPoolExecutor(0,?Integer.MAX_VALUE,
??????????????????????????????????????60L,?TimeUnit.SECONDS,
??????????????????????????????????????new?SynchronousQueue());
????}
newFixedThreadPool 是創(chuàng)建一個大小固定的線程池,線程數(shù)固定,也不會被回收
newCachedThreadPool 是創(chuàng)建一個大小為 MAX_VALUE 的線程數(shù),并具有緩存功能,如果 60 秒內(nèi)線程一直 處于空閑,則會進(jìn)行回收
另外,線程池的 shutdown 方法 doc 文檔的解釋如下:
“Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted. Invocation has no additional effect if already shut down. This method does not wait for previously submitted tasks to complete execution. Use awaitTermination to do that.
”指的是等待線程池執(zhí)行 shutdown 方法后就不再接收新的執(zhí)行目標(biāo)了,等當(dāng)前線程池中的現(xiàn)場執(zhí)行完畢后,此線程池就會關(guān)閉掉了。
通過查看 JAVA 源碼中的注釋信息后才得知原來我之前寫的代碼有了一個大大的 BUG,不應(yīng)該執(zhí)行完一次后就立即把線程池給 shutdown 掉,這樣的話,線程池的意義就沒多大的意思了,跟 new Thread 的就差不多了。尷尬了!
然后將測試代碼修改為如下的代碼:
import?org.slf4j.Logger;import?org.slf4j.LoggerFactory;
import?org.springframework.web.bind.annotation.RequestMapping;
import?org.springframework.web.bind.annotation.RequestParam;
import?org.springframework.web.bind.annotation.RestController;
?
import?java.util.concurrent.Callable;
import?java.util.concurrent.ExecutorService;
import?java.util.concurrent.Executors;
import?java.util.concurrent.FutureTask;
import?java.util.concurrent.atomic.AtomicInteger;
?
@RestController
@RequestMapping(value?=?"test")
public?class?TestController?{
????private?Logger?logger?=?LoggerFactory.getLogger(this.getClass());
?
????private?AtomicInteger?atomicInteger?=?new?AtomicInteger(0);
????final?ExecutorService?executorService?=?Executors.newCachedThreadPool();
?
????@RequestMapping(value?=?"doTest")
????public?Object?doTest(@RequestParam(defaultValue?=?"false")?Boolean?shutdown,
?????????????????????????@RequestParam(defaultValue?=?"10")?Integer?threadCount,
?????????????????????????@RequestParam(defaultValue?=?"100")?Integer?sleepTime,
?????????????????????????@RequestParam(defaultValue?=?"10")?Integer?queryCount)?{
????????long?beginTime?=?System.currentTimeMillis();
//????????final?ExecutorService?executorService?=?Executors.newFixedThreadPool(threadCount);
????????for?(int?i?=?0;?i?????????????int?finalI?=?i;
????????????Callable?callable?=?new?Callable()?{
????????????????@Override
????????????????public?Integer?call()?throws?Exception?{
????????????????????Thread.sleep(sleepTime);
????????????????????logger.debug("index:{}?threadInfo:{}",?finalI,?Thread.currentThread().toString());return?finalI;
????????????????}
????????????};
????????????FutureTask?futureTask?=?new?FutureTask(callable);
????????????executorService.submit(futureTask);
????????}if?(shutdown)?{
????????????executorService.shutdown();
????????}
????????Long?endTime?=?System.currentTimeMillis();
????????endTime?=?endTime?-?beginTime;
????????logger.info("info:{}",?endTime);return?atomicInteger.addAndGet(endTime.intValue())?+?"???this:"?+?endTime;
????}
}
調(diào)用時,shutdown 傳入 false,并且線程池的 new 方法放到上面的公共方法區(qū)域中,而不應(yīng)該是來一個請求就 new 一個線程池出來。然后將同樣的請求用 jmeter 測試后,發(fā)現(xiàn)能達(dá)到 300 多了,比之前的 20 多提升了許多倍!
總結(jié):
通過上面的測試,發(fā)現(xiàn)了一個我之前用錯的 JAVA 線程池的用法,通過 jmeter 工具測試后才知道其性能是如何的大。
同時在通過修改 springboot 的配置信息后,發(fā)現(xiàn) springBoot 能創(chuàng)建的線程池最大線程數(shù)也與其 tomcat 的最大線程數(shù)有關(guān),具體身體關(guān)系還得靠后面的慢慢探索了。(賊尷尬,這么基礎(chǔ)的代碼問題居然給犯下了這么大一個錯誤. 還好及時地修改了。哈哈)
作者:水中加點糖
來源鏈接:
https://blog.csdn.net/puhaiyang/article/details/80530495
總結(jié)
以上是生活随笔為你收集整理的springboot tomcat默认线程数_记一次JAVA线程池的错误用法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: c++ 怎样连接两个链表_LeetCod
- 下一篇: 《咏双燕诗》第六句是什么
