線程池

一、簡(jiǎn)介

一種管理線程的技術(shù)嗜闻,可以緩存線程

(1)復(fù)用已經(jīng)存在的線程蜕依,減少線程的創(chuàng)建和銷(xiāo)毀

(2)提高響應(yīng)時(shí)間,已有線程可以直接執(zhí)行任務(wù)琉雳,無(wú)需等待線程創(chuàng)建

(3)方便管理線程數(shù)量样眠,防止過(guò)多的線程占用太多內(nèi)存,同時(shí)能防止過(guò)多的線程切換翠肘,提高CPU效率

二檐束、構(gòu)造函數(shù)

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

int corePoolSize: 核心線程數(shù),在不設(shè)置 allowCoreThreadTimeOut 為 true 的情況下束倍,核心線程就算沒(méi)事做也不會(huì)被銷(xiāo)毀被丧。

int maximumPoolSize : 最大線程數(shù)

long keepAliveTime:超時(shí)時(shí)長(zhǎng),一個(gè)非核心線程(設(shè)置 allowCoreThreadTimeOut 為 true 也同樣作用于核心線程)在處于閑置狀態(tài)(沒(méi)事做)超過(guò)這個(gè)時(shí)長(zhǎng)就 會(huì)被銷(xiāo)毀绪妹。

TimeUnit unit:時(shí)間單位甥桂,秒、毫秒邮旷、微秒等

BlockingQueue<Runnable> workQueue: 阻塞隊(duì)列

ThreadFactory threadFactory:線程池工廠黄选,可以自己實(shí)現(xiàn),方便設(shè)置線程的名字以及優(yōu)先級(jí)

RejectedExecutionHandler handler:拒絕策略

image.png

三婶肩、常見(jiàn)的線程池

CachedThreadPool

可緩存線程池

Executors.newCachedThreadPool(ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>(),
                                  threadFactory);
}

1. 內(nèi)部沒(méi)有核心線程办陷,線程的數(shù)量是有沒(méi)限制的貌夕。

2. 在創(chuàng)建任務(wù)時(shí),若有空閑的線程時(shí)則復(fù)用空閑的線程懂诗,若沒(méi)有則新建線程蜂嗽。

3. 沒(méi)有工作的線程(閑置狀態(tài))在超過(guò)了60S沒(méi)有任務(wù)苗膝,就會(huì)銷(xiāo)毀殃恒。

FixedThreadPool

定長(zhǎng)線程池

Executors.newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
   return new ThreadPoolExecutor(nThreads, nThreads,
                                 0L, TimeUnit.MILLISECONDS,
                                 new LinkedBlockingQueue<Runnable>(),
                                 threadFactory);
}

1. 最大線程數(shù)等于核心線程數(shù),所以在默認(rèn)情況下辱揭,該線程池的線程不會(huì)因?yàn)殚e置狀態(tài)超時(shí)而被銷(xiāo)毀离唐。

2. 如果當(dāng)前線程數(shù)小于核心線程數(shù),并且也有閑置線程的時(shí)候提交了任務(wù)问窃,這時(shí)也不會(huì)去復(fù)用之前的閑置線程亥鬓,會(huì)創(chuàng)建新的線程去執(zhí)行任務(wù)。如果當(dāng)前執(zhí)行任務(wù)數(shù)大于了核心線程數(shù)域庇,大于的部分就會(huì)進(jìn)入隊(duì)列等待嵌戈。等著有閑置的線程來(lái)執(zhí)行這個(gè)任務(wù)。LinkedBlockingQueue默認(rèn)大小是Integer.MAX_VALUE听皿,可能導(dǎo)致內(nèi)存占用過(guò)多

ScheduledThreadPool

定時(shí)任務(wù)線程池

public static ScheduledExecutorService newScheduledThreadPool(
        int corePoolSize, ThreadFactory threadFactory) {
    return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}

public ScheduledThreadPoolExecutor(int corePoolSize,
                                   ThreadFactory threadFactory,
                                   RejectedExecutionHandler handler) {
    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue(), threadFactory, handler);
}

1. 設(shè)置了核心線程數(shù)熟呛,最大線程數(shù)也是 Integer.MAX_VALUE。

2. 這個(gè)線程池是上述4個(gè)中為唯一個(gè)有延遲執(zhí)行和周期執(zhí)行任務(wù)的線程池尉姨。

SingleThreadExecutor

單線程池

public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>(),
                                threadFactory));
}

1庵朝、只有一個(gè)線程且不回收

2、LinkedBlockingQueue默認(rèn)大小是Integer.MAX_VALUE又厉,可能導(dǎo)致內(nèi)存占用過(guò)多

四九府、阻塞隊(duì)列

常用的幾種隊(duì)列

(1)ArrayBlockingQueue:基于數(shù)組實(shí)現(xiàn),固定大小覆致,其構(gòu)造必須指定大小侄旬。其所含的對(duì)象是FIFO順序排序的。

(2)LinkedBlockingQueue:基于鏈表實(shí)現(xiàn)煌妈,默認(rèn)大小Integer.MAX_VALUE勾怒,可傳入。其所含的對(duì)象是FIFO順序排序的声旺。

(3)PriorityBlockingQueue:類(lèi)似于LinkedBlockingQueue笔链,但是其所含對(duì)象的排序不是FIFO,而是依據(jù)對(duì)象的自然順序或者構(gòu)造函數(shù)的Comparator決定腮猖。無(wú)界隊(duì)列

(4)SynchronousQueue:不存儲(chǔ)元素的阻塞隊(duì)列鉴扫,大小為0,每一個(gè)put都必須等待take操作澈缺,提供任務(wù)的轉(zhuǎn)發(fā)坪创。

(5) DelayQueue:基于PriorityQueue炕婶, 支持延時(shí)獲取元素, 無(wú)界

常用方法

拋異常 返回特殊值 一直阻塞 超時(shí)退出
插入數(shù)據(jù) add(e) offer(e) put(e) offer(e, time, unit)
移除數(shù)據(jù) remove(e) poll() take() poll(time, unit)

take:

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == 0)
            notEmpty.await();
        return dequeue();
    } finally {
        lock.unlock();
    }
}

五莱预、拒絕策略

1柠掂、拒絕時(shí)機(jī)

  • 線程池沒(méi)有能力處理新任務(wù):最大線程數(shù)大于且隊(duì)列數(shù)量到達(dá)了最大值

  • 線程池被調(diào)用了shutdown關(guān)閉之后,還沒(méi)有完全停止的時(shí)候依沮。

2涯贞、策略

  • AbortPolicy:默認(rèn)的策略,直接拋出RejectedExecutionException ,RuntimeException

  • DiscardPolicy:默默丟棄任務(wù)危喉,不進(jìn)行任何通知宋渔。實(shí)際上內(nèi)部啥也沒(méi)干

  • DiscardOldestPolicy:丟棄掉在隊(duì)列中存在時(shí)間最久的任務(wù),然后重新提交任務(wù)

  • CallerRunsPolicy:讓提交任務(wù)的線程去執(zhí)行任務(wù)辜限,不會(huì)丟失業(yè)務(wù)數(shù)據(jù)皇拣,阻塞了提交任務(wù)的線程,減緩提交任務(wù)的速度

3薄嫡、拒絕策略使用

關(guān)鍵業(yè)務(wù)要自定義拒絕策略氧急,可以記錄日志,也可以把拒絕結(jié)果返回給用戶

六毫深、合理使用線程池

java 開(kāi)發(fā)手冊(cè)<并發(fā)處理>中片段:

[圖片上傳失敗...(image-cdc18e-1600494356170)]

1吩坝、自定義線程池時(shí),使用自定義ThreadFactory费什,指定線程名钾恢,方便排查問(wèn)題

2、分析業(yè)務(wù)數(shù)量級(jí)鸳址,如果會(huì)很大瘩蚪,必須自定義線程池,限定最大線程池稿黍,使用有界隊(duì)列疹瘦,防止jvm OOM

3、即使限制了最大線程數(shù)巡球,過(guò)多的線程會(huì)導(dǎo)致線程切換頻繁言沐,cpu效率地下,也要考慮為別的業(yè)務(wù)讓出cpu

4酣栈、核心線程數(shù)险胰,看任務(wù),如果時(shí)一天執(zhí)行一次矿筝,設(shè)置為0起便,跑完釋放內(nèi)存。如果常用,看任務(wù)量

5榆综、拒絕策略要重寫(xiě)妙痹,默認(rèn)的策略AbortPolicy會(huì)導(dǎo)致crash

七、線程池的復(fù)用原理

1鼻疮、回顧線程創(chuàng)建

new Thread(new Runnable() {
    @Override
    public void run() {
        
    }
}).start();

創(chuàng)建一個(gè)線程怯伊,調(diào)用start()啟動(dòng)線程,線程執(zhí)行完run方法判沟,就進(jìn)入Dead狀態(tài)耿芹。start()方法只能調(diào)用一次

2、線程池提交任務(wù)

ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(new Runnable() {
    @Override
    public void run() {
        
    }
});

3水评、復(fù)用原理

復(fù)用線程的本質(zhì):提交任務(wù)猩系,在任務(wù)內(nèi)死循環(huán)執(zhí)行提交下來(lái)的任務(wù)

public void run() {
  while (true) {
    if(tasks available) {
      Runnable task = taskqueue.dequeue();
      task.run();
    } else{
      // wait or shutdown
    }
  }
}

線程創(chuàng)建執(zhí)行任務(wù)流程:

image.png

(1) execute提交任務(wù)

判斷當(dāng)前工作線程數(shù)是否小于核心線程數(shù)媚送,需要?jiǎng)?chuàng)建新線程則進(jìn)入addWorker

(2) addWorker 創(chuàng)建線程

  private final class Worker implements Runnable{
      final Thread thread;
      Runnable firstTask;
      Worker(Runnable firstTask) {
        this.firstTask = firstTask;
        // 把 Worker 作為 thread 運(yùn)行的任務(wù)
        this.thread = getThreadFactory().newThread(this);
    }
      public void run() {
          runWorker(this);
      }
  }

(3)創(chuàng)建Worker

Worker以提交下來(lái)的任務(wù)作為構(gòu)造參數(shù)中燥,并創(chuàng)建一個(gè)新的線程

w = new Worker(firstTask);
final Thread t = w.thread;
workers.add(w);

(4)runWorker

調(diào)用Worker內(nèi)部變量thread的start()方法啟動(dòng)線程

final Thread t = worker.thread;
 t.start();

線程啟動(dòng)后,調(diào)用到Worker內(nèi)部的run方法

  public void run() {
          runWorker(this);
      }

(5)循環(huán)執(zhí)行任務(wù)

final void runWorker(Worker w) {
    Runnable task = w.firstTask;
    while (task != null || (task = getTask()) != null) {
        try {
            task.run();
        } finally {
            task = null;
        }
    }
}
Runnable r = timed ?
    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
    workQueue.take()

八塘偎、關(guān)閉線程池

shutdown() : 調(diào)用之后疗涉,線程池不是立刻就關(guān)閉,等待任務(wù)隊(duì)列中的任務(wù)執(zhí)行完才徹底關(guān)閉吟秩。同時(shí)咱扣,還會(huì)根據(jù)拒絕策略拒絕新提交的任務(wù)

isShutdown(): 返回線程池是否已經(jīng)開(kāi)始了關(guān)閉工作,返回不代表已經(jīng)徹底關(guān)閉涵防。

isTerminated(): 檢測(cè)線程池是否真正的關(guān)閉了

shutdownNow(): 強(qiáng)制關(guān)閉線程池闹伪,給所有線程發(fā)送interrupt中斷信號(hào),然后所有的任務(wù)轉(zhuǎn)移到另外一個(gè)List中壮池。方便調(diào)用這做一些補(bǔ)救操作偏瓤。

參考文檔

Java 阻塞隊(duì)列--BlockingQueue: https://www.cnblogs.com/bjxq-cs88/p/9759571.html

Java 線程池中的線程復(fù)用是如何實(shí)現(xiàn):http://www.reibang.com/p/f84a61917b03

Tomcat 高并發(fā)之道原理拆解與性能調(diào)優(yōu):https://mp.weixin.qq.com/s/4_En4hHeolYyO0RcliGxCQ

Java線程池的拒絕策略:https://www.cnblogs.com/eric-fang/p/11584142.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市椰憋,隨后出現(xiàn)的幾起案子厅克,更是在濱河造成了極大的恐慌,老刑警劉巖橙依,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件证舟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡窗骑,警方通過(guò)查閱死者的電腦和手機(jī)女责,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)创译,“玉大人抵知,你說(shuō)我怎么就攤上這事。” “怎么了辛藻?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵碘橘,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我吱肌,道長(zhǎng)痘拆,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任氮墨,我火速辦了婚禮纺蛆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘规揪。我一直安慰自己桥氏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布猛铅。 她就那樣靜靜地躺著字支,像睡著了一般。 火紅的嫁衣襯著肌膚如雪奸忽。 梳的紋絲不亂的頭發(fā)上堕伪,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音栗菜,去河邊找鬼欠雌。 笑死,一個(gè)胖子當(dāng)著我的面吹牛疙筹,可吹牛的內(nèi)容都是我干的富俄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼而咆,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼霍比!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起翘盖,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤桂塞,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后馍驯,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體阁危,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年汰瘫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了狂打。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡混弥,死狀恐怖趴乡,靈堂內(nèi)的尸體忽然破棺而出对省,到底是詐尸還是另有隱情,我是刑警寧澤晾捏,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布蒿涎,位于F島的核電站,受9級(jí)特大地震影響惦辛,放射性物質(zhì)發(fā)生泄漏劳秋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一胖齐、第九天 我趴在偏房一處隱蔽的房頂上張望玻淑。 院中可真熱鬧,春花似錦呀伙、人聲如沸补履。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)箫锤。三九已至,卻和暖如春驰弄,著一層夾襖步出監(jiān)牢的瞬間麻汰,已是汗流浹背速客。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工戚篙, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人溺职。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓岔擂,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親浪耘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子乱灵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353