并發(fā)編程系列之如何正確使用線程池?

并發(fā)編程系列博客

原文鏈接

并發(fā)編程系列之如何正確使用線程池续徽?在上一章節(jié)的學(xué)習(xí)中,我們掌握了線程的基本知識(shí)亲澡,接著本博客會(huì)繼續(xù)學(xué)習(xí)多線程中的線程池知識(shí)

1钦扭、線程是不是越多越好?

在學(xué)習(xí)多線程之前床绪,讀者可能會(huì)有疑問(wèn)客情?如果單線程跑得太慢,那么是否就能多創(chuàng)建多個(gè)線程來(lái)跑任務(wù)癞己?并發(fā)的情況膀斋,線程是不是創(chuàng)建越多越好?這是一個(gè)很經(jīng)典的問(wèn)題痹雅,畫(huà)圖表示一下創(chuàng)建很多線程的情況仰担,然后進(jìn)行情況分析。


在這里插入圖片描述
  • 創(chuàng)建線程和銷(xiāo)毀線程都是需要時(shí)間的练慕,如果創(chuàng)建時(shí)間+銷(xiāo)毀時(shí)間>執(zhí)行任務(wù)時(shí)間就很不劃算
  • 創(chuàng)建后的線程是需要內(nèi)存去存放的惰匙,創(chuàng)建的線程對(duì)應(yīng)一個(gè)Thread對(duì)象,對(duì)象是會(huì)占用JVM的堆內(nèi)存的铃将,根據(jù)jvm規(guī)范项鬼,一個(gè)線程默認(rèn)最大棧大小為1M,這個(gè)椌⒀郑空間也是需要從系統(tǒng)內(nèi)存中分配的绘盟,所以線程越多,需要的內(nèi)存就越多
  • 創(chuàng)建線程,操作系統(tǒng)是需要頻繁進(jìn)行線程上下文切換的龄毡,所以線程創(chuàng)建太多吠卷,是會(huì)影響性能的

上下文切換(context switch):對(duì)于單核CPU來(lái)說(shuō),在一個(gè)時(shí)刻只能運(yùn)行一個(gè)線程沦零,對(duì)于并行來(lái)說(shuō)祭隔,單核cpu也是可以支持多線程執(zhí)行代碼的,CPU是通過(guò)給線程分配時(shí)間片來(lái)解決的路操,所謂時(shí)間片是CPU給每個(gè)線程分配的時(shí)間疾渴,時(shí)間片的時(shí)間是非常短的,所以執(zhí)行完成一個(gè)時(shí)間片后屯仗,進(jìn)行任務(wù)切換搞坝,切換之前先保存這個(gè)任務(wù)的狀態(tài),以便于下次換回來(lái)的時(shí)候魁袜,可以加載這個(gè)任務(wù)的狀態(tài)桩撮,所以從保存任務(wù)狀態(tài)到再加載任務(wù)的過(guò)程稱(chēng)為上下文切換,不僅在線程間可以上下文切換峰弹,進(jìn)程也同樣可以

2店量、如何正確使用多線程?

  • 如果是計(jì)算型任務(wù)鞠呈?
    CPU數(shù)量的1~2倍即可
  • 如果是IO密集型任務(wù)垫桂?
    就需要多一些線程,要根據(jù)具體的io阻塞時(shí)長(zhǎng)來(lái)進(jìn)行考量決定

3粟按、Java線程池的工作原理

在這里插入圖片描述
  • 接收任務(wù)诬滩,放入線程池的任務(wù)倉(cāng)庫(kù)
  • 工作線程從線程池的任務(wù)倉(cāng)庫(kù)取,執(zhí)行
  • 沒(méi)有任務(wù)時(shí)灭将,線程阻塞疼鸟,有任務(wù)時(shí)喚醒線程

4、掌握J(rèn)UC線程池API

  • Executor : 接口類(lèi)

  • ExecutorService:加入關(guān)閉方法和對(duì)Runnable庙曙、Callable空镜、Future的支持


    在這里插入圖片描述
    • shutdown:已經(jīng)提交的會(huì)執(zhí)行完成
    • shutdownNow:正在執(zhí)行的會(huì)執(zhí)行完成,未來(lái)執(zhí)行的返回
    • awaitTermination:阻塞等待任務(wù)關(guān)閉完成
    • submit類(lèi)型的:都是提交任務(wù)的捌朴,支持Runnable和Callable
    • invokeAll類(lèi)型的:執(zhí)行集合中所有任務(wù)
  • ScheduleExecutorService :加入對(duì)定時(shí)任務(wù)的支持
    在這里插入圖片描述

    其中schedule(Runablle , long, Timeunit)schedule(Callable<V> , long, TimeUnit)表示的是多久后執(zhí)行吴攒,而scheduleAtFixedRate方法和scheduleWithFixedDelay方法表示的都是周期性重復(fù)執(zhí)行的

再描述scheduleAtFixedRate方法和scheduleWithFixedDelay方法的區(qū)別:
scheduleAtFixedRate:以固定的時(shí)間頻率重復(fù)執(zhí)行任務(wù),如每10s 砂蔽,也就是兩個(gè)任務(wù)直接以固定的時(shí)間間隔執(zhí)行洼怔,不管任務(wù)執(zhí)行完成與否

在這里插入圖片描述

scheduleWithFixedDelay:以固定的任務(wù)時(shí)延遲來(lái)重復(fù)執(zhí)行任務(wù),這種任務(wù)不管任務(wù)執(zhí)行多久都執(zhí)行完成左驾,然后隔預(yù)定的如3s镣隶,接著執(zhí)行下一個(gè)任務(wù)极谊,每個(gè)任務(wù)之間的間隔都是一樣的

在這里插入圖片描述
  • Executors:快速得到線程池的工具類(lèi),創(chuàng)建線程池的工廠類(lèi)
    • newFixedThreadPool(int nThreads):創(chuàng)建一個(gè)固定大小安岂、任務(wù)隊(duì)列 無(wú)界的線程池轻猖。線程池的核心線程數(shù)=最大線程池=nThreads
    • newCachedThreadPool():創(chuàng)建的是一個(gè)大小無(wú)界的緩沖線程池。它的任務(wù)隊(duì)列是一個(gè)同步隊(duì)列域那。如果隊(duì)列中有空閑的線程咙边,則用空閑線程執(zhí)行,如果沒(méi)有就創(chuàng)建新線程執(zhí)行次员。池中線程空閑超過(guò)60s样眠,就會(huì)被釋放。緩沖線程池使用于執(zhí)行耗時(shí)比較小的異步任務(wù)翠肘。線程池的核心線程數(shù)=0,最大線程池=Integer.MAX_VALUE
    • newSingleThreadExecutor():創(chuàng)建的是只有一個(gè)線程來(lái)執(zhí)行無(wú)界任務(wù)隊(duì)列的單一線程池辫秧。該線程池按順序執(zhí)行一個(gè)一個(gè)加入的任務(wù)束倍,任何時(shí)刻都只有一個(gè)線程在執(zhí)行。單一線程池和newFixedThreadPool(1)的區(qū)別在于盟戏,單一線程池的池大小是不能再改變的
    • newScheduleThreadPool(int corePoolSize): 能定時(shí)執(zhí)行任務(wù)的線程池绪妹,該池的核心線程數(shù)由參數(shù)corePoolSize指定,最大線程數(shù)=Integer.MAX_VALUE
    • newWorkStealingPool():以當(dāng)前系統(tǒng)可用的處理器數(shù)作為并行級(jí)別創(chuàng)建的work-stealing thread pool(ForkJoinPool)
    • newWorkStealingPool(int parallelism):以指定的parallelism并行級(jí)別創(chuàng)建的work-stealing thread pool(ForkJoinPool)
  • ThreadPoolExecutor:線程池的標(biāo)準(zhǔn)實(shí)現(xiàn)


    在這里插入圖片描述

    下面列舉出ThreadPoolExecutor的主要參數(shù):

參數(shù) 描述
corePoolSize 核心線程數(shù)量
maxPoolSize 最大線程數(shù)量
keepAliveTime+時(shí)間單位 空閑線程的存活時(shí)間
ThreadFactory 線程工廠柿究,用于創(chuàng)建線程
workQueue 用于存放任務(wù)的隊(duì)列邮旷,可以稱(chēng)之為工作隊(duì)列
Handler 用于處理被拒絕的任務(wù)

雖然Executors使用起來(lái)很方便,不過(guò)在阿里編程規(guī)范里是強(qiáng)調(diào)了慎用Executors創(chuàng)建線程池蝇摸,下面摘錄自阿里編程規(guī)范手冊(cè):

【強(qiáng)制】線程池不允許使用Executors去創(chuàng)建婶肩,而是通過(guò)ThreadPoolExecutor的方式,
這樣的處理方式讓寫(xiě)的同學(xué)更加明確線程池的運(yùn)行規(guī)則貌夕,規(guī)避資源耗盡的風(fēng)險(xiǎn)律歼。
說(shuō)明:Executors各個(gè)方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
??主要問(wèn)題是堆積的請(qǐng)求處理隊(duì)列可能會(huì)耗費(fèi)非常大的內(nèi)存,甚至OOM啡专。
2)newCachedThreadPool和newScheduledThreadPool:
??主要問(wèn)題是線程數(shù)最大數(shù)是Integer.MAX_VALUE险毁,可能會(huì)創(chuàng)建數(shù)量非常多的線程,甚至OOM们童。

ThreadPoolExecutor的基本參數(shù):

new ThreadPoolExecutor(
                2, // 核心線程數(shù) 
                5, // 最大線程數(shù)
                60L, // keepAliveTime,線程空閑超過(guò)這個(gè)數(shù)畔况,就會(huì)被銷(xiāo)毀釋放
                TimeUnit.SECONDS, // keepAliveTime的時(shí)間單位
                new ArrayBlockingQueue(5)); // 傳入邊界為5的工作隊(duì)列

畫(huà)流程圖表示,線程池的核心參數(shù)是corePoolSize慧库、maxPoolSize跷跪、workQueue(工作隊(duì)列)


在這里插入圖片描述

線程池工作原理示意圖:任務(wù)可以一直放,直到線程池滿了的情況齐板,才會(huì)拒絕域庇,然后除了核心線程嵌戈,其它的線程會(huì)被合理回收。所以正常情況下听皿,線程池中的線程數(shù)量會(huì)處在 corePoolSize 與 maximumPoolSize 的閉區(qū)間內(nèi)


在這里插入圖片描述

ThreadPoolExecutor基本實(shí)例:
ExecutorService service = new ThreadPoolExecutor(2, 5,
                60L, TimeUnit.SECONDS,
                new ArrayBlockingQueue(5));
 service.execute(() ->{
     System.out.println(String.format("thread name:%s",Thread.currentThread().getName()));
 });
  // 避免內(nèi)存泄露熟呛,記得關(guān)閉線程池
 service.shutdown();

ThreadPoolExecutor加上Callable、Future使用的例子:

public static void  main(String[] args) {
    ExecutorService service = new ThreadPoolExecutor(2, 5,
                    60L, TimeUnit.SECONDS,
                    new ArrayBlockingQueue(5));
    
    Future<Integer> future = service.submit(new CallableTask());
    Thread.sleep(3000);
    System.out.println("future is done?" + future.isDone());
    if (future.isDone()) {
        System.out.println("callableTask返回參數(shù):"+future.get());
    }
    service.shutdown();
}

static class CallableTask implements Callable<Integer>{
     @Override
     public Integer call() {
         return ThreadLocalRandom.current().ints(0, (99 + 1)).limit(1).findFirst().getAsInt();
     }
 }

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末尉姨,一起剝皮案震驚了整個(gè)濱河市庵朝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌又厉,老刑警劉巖九府,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異覆致,居然都是意外死亡侄旬,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)煌妈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)儡羔,“玉大人,你說(shuō)我怎么就攤上這事璧诵√” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵之宿,是天一觀的道長(zhǎng)族操。 經(jīng)常有香客問(wèn)我,道長(zhǎng)比被,這世上最難降的妖魔是什么色难? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮等缀,結(jié)果婚禮上莱预,老公的妹妹穿的比我還像新娘。我一直安慰自己项滑,他們只是感情好依沮,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著枪狂,像睡著了一般危喉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上州疾,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天辜限,我揣著相機(jī)與錄音,去河邊找鬼严蓖。 笑死薄嫡,一個(gè)胖子當(dāng)著我的面吹牛氧急,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播毫深,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼吩坝,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了哑蔫?” 一聲冷哼從身側(cè)響起钉寝,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎闸迷,沒(méi)想到半個(gè)月后嵌纲,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡腥沽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年逮走,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片今阳。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡师溅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出酣栈,到底是詐尸還是另有隱情,我是刑警寧澤汹押,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布矿筝,位于F島的核電站,受9級(jí)特大地震影響棚贾,放射性物質(zhì)發(fā)生泄漏窖维。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一妙痹、第九天 我趴在偏房一處隱蔽的房頂上張望铸史。 院中可真熱鬧,春花似錦怯伊、人聲如沸琳轿。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)崭篡。三九已至,卻和暖如春吧秕,著一層夾襖步出監(jiān)牢的瞬間琉闪,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工砸彬, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留颠毙,地道東北人斯入。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蛀蜜,于是被迫代替她去往敵國(guó)和親刻两。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容