線程池--原理深析

我們使用線程的時候就去創(chuàng)建一個線程榔至,這樣實現(xiàn)起來非常簡便耗绿,但是就會有一個問題:
如果并發(fā)的線程數(shù)量很多杨箭,并且每個線程都是執(zhí)行一個時間很短的任務(wù)就結(jié)束了膝蜈,這樣頻繁創(chuàng)建線程就會大大降低系統(tǒng)的效率鹅心,因為頻繁創(chuàng)建線程和銷毀線程需要時間吕粗。
那么有沒有一種辦法使得線程可以復(fù)用,就是執(zhí)行完一個任務(wù)旭愧,并不被銷毀颅筋,而是可以繼續(xù)執(zhí)行其他的任務(wù)虐秋?
在Java中可以通過線程池來達到這樣的效果。今天我們就來詳細講解一下Java的線程池垃沦,首先我們從最核心的ThreadPoolExecutor類中的方法講起客给,然后再講述它的實現(xiàn)原理,接著給出了它的使用示例肢簿,最后討論了一下如何合理配置線程池的大小靶剑。
以下是本文的目錄大綱:
一.Java中的ThreadPoolExecutor類
二.深入剖析線程池實現(xiàn)原理
三.使用示例
四.如何合理配置線程池的大小

若有不正之處請多多諒解,并歡迎批評指正池充。

一.Java中的ThreadPoolExecutor類

二.深入剖析線程池實現(xiàn)原理

在上一節(jié)我們從宏觀上介紹了ThreadPoolExecutor桩引,下面我們來深入解析一下線程池的具體實現(xiàn)原理,將從下面幾個方面講解:
1.線程池狀態(tài)
2.任務(wù)的執(zhí)行
3.線程池中的線程初始化
4.任務(wù)緩存隊列及排隊策略
5.任務(wù)拒絕策略
6.線程池的關(guān)閉
7.線程池容量的動態(tài)調(diào)整

1.線程池狀態(tài)
在ThreadPoolExecutor中定義了一個Volatile變量收夸,另外定義了幾個static final變量表示線程池的各個狀態(tài):

volatile int runState;
static final int RUNNING    = 0;
static final int SHUTDOWN   = 1;
static final int STOP       = 2;
static final int TERMINATED = 3;

runState表示當前線程池的狀態(tài)坑匠,它是一個volatile變量用來保證線程之間的可見性;

下面的幾個static final變量表示runState可能的幾個取值卧惜。

當創(chuàng)建線程池后厘灼,初始時,線程池處于RUNNING狀態(tài)咽瓷;

如果調(diào)用了shutdown()方法设凹,則線程池處于SHUTDOWN狀態(tài),此時線程池不能夠接受新的任務(wù)茅姜,它會等待所有任務(wù)執(zhí)行完畢闪朱;

如果調(diào)用了shutdownNow()方法,則線程池處于STOP狀態(tài)钻洒,此時線程池不能接受新的任務(wù)奋姿,并且會去嘗試終止正在執(zhí)行的任務(wù);

當線程池處于SHUTDOWN或STOP狀態(tài)素标,并且所有工作線程已經(jīng)銷毀称诗,任務(wù)緩存隊列已經(jīng)清空或執(zhí)行結(jié)束后,線程池被設(shè)置為TERMINATED狀態(tài)糯钙。

2.任務(wù)的執(zhí)行

在了解將任務(wù)提交給線程池到任務(wù)執(zhí)行完畢整個過程之前粪狼,我們先來看一下ThreadPoolExecutor類中其他的一些比較重要成員變量:

private final BlockingQueue<Runnable> workQueue;              //任務(wù)緩存隊列退腥,用來存放等待執(zhí)行的任務(wù)
private final ReentrantLock mainLock = new ReentrantLock();   //線程池的主要狀態(tài)鎖任岸,對線程池狀態(tài)(比如線程池大小
                                                              //、runState等)的改變都要使用這個鎖
private final HashSet<Worker> workers = new HashSet<Worker>();  //用來存放工作集

private volatile long  keepAliveTime;    //線程存活時間   
private volatile boolean allowCoreThreadTimeOut;   //是否允許為核心線程設(shè)置存活時間
private volatile int   corePoolSize;     //核心池的大薪屏酢(即線程池中的線程數(shù)目大于這個參數(shù)時享潜,提交的任務(wù)會被放進任務(wù)緩存隊列)
private volatile int   maximumPoolSize;   //線程池最大能容忍的線程數(shù)

private volatile int   poolSize;       //線程池中當前的線程數(shù)

private volatile RejectedExecutionHandler handler; //任務(wù)拒絕策略

private volatile ThreadFactory threadFactory;   //線程工廠,用來創(chuàng)建線程

private int largestPoolSize;   //用來記錄線程池中曾經(jīng)出現(xiàn)過的最大線程數(shù)

private long completedTaskCount;   //用來記錄已經(jīng)執(zhí)行完畢的任務(wù)個數(shù)

每個變量的作用都已經(jīng)標明出來了嗅蔬,這里要重點解釋一下corePoolSize剑按、maximumPoolSize疾就、largestPoolSize三個變量。

corePoolSize在很多地方被翻譯成核心池大小艺蝴,其實我的理解這個就是線程池的大小猬腰。
largestPoolSize只是一個用來起記錄作用的變量,用來記錄線程池中曾經(jīng)有過的最大線程數(shù)目猜敢,跟線程池的容量沒有任何關(guān)系姑荷。

下面我們進入正題,看一下任務(wù)從提交到最終執(zhí)行完畢經(jīng)歷了哪些過程缩擂。

在ThreadPoolExecutor類中鼠冕,最核心的任務(wù)提交方法是execute()方法,雖然通過submit也可以提交任務(wù)胯盯,但是實際上submit方法里面最終調(diào)用的還是execute()方法懈费,所以我們只需要研究execute()方法的實現(xiàn)原理即可:

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
        if (runState == RUNNING && workQueue.offer(command)) {
            if (runState != RUNNING || poolSize == 0)
                ensureQueuedTaskHandled(command);
        }
        else if (!addIfUnderMaximumPoolSize(command))
            reject(command); // is shutdown or saturated
    }
}

上面的代碼可能看起來不是那么容易理解,下面我們一句一句解釋:

首先博脑,判斷提交的任務(wù)command是否為null憎乙,若是null,則拋出空指針異常叉趣;

接著是這句寨闹,這句要好好理解一下:

if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command))

由于是或條件運算符,所以先計算前半部分的值君账,如果線程池中當前線程數(shù)不小于核心池大小,那么就會直接進入下面的if語句塊了乡数。

如果線程池中當前線程數(shù)小于核心池大小,則接著執(zhí)行后半部分净赴,也就是執(zhí)行:

addIfUnderCorePoolSize(command)

如果執(zhí)行完addIfUnderCorePoolSize這個方法返回false,則繼續(xù)執(zhí)行下面的if語句塊玖翅,否則整個方法就直接執(zhí)行完畢了。

如果執(zhí)行完addIfUnderCorePoolSize這個方法返回false金度,然后接著判斷:

if (runState == RUNNING && workQueue.offer(command))

如果當前線程池處于RUNNING狀態(tài),則將任務(wù)放入任務(wù)緩存隊列猜极;如果當前線程池不處于RUNNING狀態(tài)或者任務(wù)放入緩存隊列失敗,則執(zhí)行:

addIfUnderMaximumPoolSize(command)

如果執(zhí)行addIfUnderMaximumPoolSize方法失敗跟伏,則執(zhí)行reject()方法進行任務(wù)拒絕處理翩瓜。

回到前面:

if (runState == RUNNING && workQueue.offer(command))

這句的執(zhí)行携龟,如果說當前線程池處于RUNNING狀態(tài)且將任務(wù)放入任務(wù)緩存隊列成功,則繼續(xù)進行判斷:

if (runState != RUNNING || poolSize == 0)

這句判斷是為了防止在將此任務(wù)添加進任務(wù)緩存隊列的同時其他線程突然調(diào)用shutdown或者shutdownNow方法關(guān)閉了線程池的一種應(yīng)急措施峡蟋。如果是這樣就執(zhí)行:

ensureQueuedTaskHandled(command)

進行應(yīng)急處理浮定,從名字可以看出是保證 添加到任務(wù)緩存隊列中的任務(wù)得到處理。

我們接著看2個關(guān)鍵方法的實現(xiàn):addIfUnderCorePoolSize和addIfUnderMaximumPoolSize:

private boolean addIfUnderCorePoolSize(Runnable firstTask) {
    Thread t = null;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        if (poolSize < corePoolSize && runState == RUNNING)
            t = addThread(firstTask);        //創(chuàng)建線程去執(zhí)行firstTask任務(wù)   
        } finally {
        mainLock.unlock();
    }
    if (t == null)
        return false;
    t.start();
    return true;
}

這個是addIfUnderCorePoolSize方法的具體實現(xiàn)层亿,從名字可以看出它的意圖就是當?shù)陀诤诵某源笮r執(zhí)行的方法桦卒。下面看其具體實現(xiàn),首先獲取到鎖匿又,因為這地方涉及到線程池狀態(tài)的變化方灾,先通過if語句判斷當前線程池中的線程數(shù)目是否小于核心池大小,有朋友也許會有疑問:前面在execute()方法中不是已經(jīng)判斷過了嗎碌更,只有線程池當前線程數(shù)目小于核心池大小才會執(zhí)行addIfUnderCorePoolSize方法的裕偿,為何這地方還要繼續(xù)判斷?原因很簡單痛单,前面的判斷過程中并沒有加鎖嘿棘,因此可能在execute方法判斷的時候poolSize小于corePoolSize,而判斷完之后旭绒,在其他線程中又向線程池提交了任務(wù)鸟妙,就可能導(dǎo)致poolSize不小于corePoolSize了,所以需要在這個地方繼續(xù)判斷挥吵。然后接著判斷線程池的狀態(tài)是否為RUNNING重父,原因也很簡單,因為有可能在其他線程中調(diào)用了shutdown或者shutdownNow方法忽匈。然后就是執(zhí)行

t = addThread(firstTask);

這個方法也非常關(guān)鍵房午,傳進去的參數(shù)為提交的任務(wù),返回值為Thread類型丹允。然后接著在下面判斷t是否為空郭厌,為空則表明創(chuàng)建線程失敗(即poolSize>=corePoolSize或者runState不等于RUNNING)雕蔽,否則調(diào)用t.start()方法啟動線程折柠。

我們來看一下addThread方法的實現(xiàn):

private Thread addThread(Runnable firstTask) {
    Worker w = new Worker(firstTask);
    Thread t = threadFactory.newThread(w);  //創(chuàng)建一個線程,執(zhí)行任務(wù)   
    if (t != null) {
        w.thread = t;            //將創(chuàng)建的線程的引用賦值為w的成員變量       
        workers.add(w);
        int nt = ++poolSize;     //當前線程數(shù)加1       
        if (nt > largestPoolSize)
            largestPoolSize = nt;
    }
    return t;
}

在addThread方法中萎羔,首先用提交的任務(wù)創(chuàng)建了一個Worker對象液走,然后調(diào)用線程工廠threadFactory創(chuàng)建了一個新的線程t,然后將線程t的引用賦值給了Worker對象的成員變量thread缘眶,接著通過workers.add(w)將Worker對象添加到工作集當中巷懈。

下面我們看一下Worker類的實現(xiàn):

private final class Worker implements Runnable {
    private final ReentrantLock runLock = new ReentrantLock();
    private Runnable firstTask;
    volatile long completedTasks;
    Thread thread;
    Worker(Runnable firstTask) {
        this.firstTask = firstTask;
    }
    boolean isActive() {
        return runLock.isLocked();
    }
    void interruptIfIdle() {
        final ReentrantLock runLock = this.runLock;
        if (runLock.tryLock()) {
            try {
        if (thread != Thread.currentThread())
        thread.interrupt();
            } finally {
                runLock.unlock();
            }
        }
    }
    void interruptNow() {
        thread.interrupt();
    }

    private void runTask(Runnable task) {
        final ReentrantLock runLock = this.runLock;
        runLock.lock();
        try {
            if (runState < STOP &&
                Thread.interrupted() &&
                runState >= STOP)
            boolean ran = false;
            beforeExecute(thread, task);   //beforeExecute方法是ThreadPoolExecutor類的一個方法顶燕,沒有具體實現(xiàn)涌攻,用戶可以根據(jù)
            //自己需要重載這個方法和后面的afterExecute方法來進行一些統(tǒng)計信息恳谎,比如某個任務(wù)的執(zhí)行時間等           
            try {
                task.run();
                ran = true;
                afterExecute(task, null);
                ++completedTasks;
            } catch (RuntimeException ex) {
                if (!ran)
                    afterExecute(task, ex);
                throw ex;
            }
        } finally {
            runLock.unlock();
        }
    }

    public void run() {
        try {
            Runnable task = firstTask;
            firstTask = null;
            while (task != null || (task = getTask()) != null) {
                runTask(task);
                task = null;
            }
        } finally {
            workerDone(this);   //當任務(wù)隊列中沒有任務(wù)時因痛,進行清理工作       
        }
    }
}

它實際上實現(xiàn)了Runnable接口鸵膏,因此上面的Thread t = threadFactory.newThread(w);效果跟下面這句的效果基本一樣:

Thread t = new Thread(w);

相當于傳進去了一個Runnable任務(wù)谭企,在線程t中執(zhí)行這個Runnable赞咙。

既然Worker實現(xiàn)了Runnable接口攀操,那么自然最核心的方法便是run()方法了:

public void run() {
    try {
        Runnable task = firstTask;
        firstTask = null;
        while (task != null || (task = getTask()) != null) {
            runTask(task);
            task = null;
        }
    } finally {
        workerDone(this);
    }
}

從run方法的實現(xiàn)可以看出秸抚,它首先執(zhí)行的是通過構(gòu)造器傳進來的任務(wù)firstTask剥汤,在調(diào)用runTask()執(zhí)行完firstTask之后吭敢,在while循環(huán)里面不斷通過getTask()去取新的任務(wù)來執(zhí)行,那么去哪里取呢欲低?自然是從任務(wù)緩存隊列里面去取砾莱,getTask是ThreadPoolExecutor類中的方法,并不是Worker類中的方法聚假,下面是getTask方法的實現(xiàn):

Runnable getTask() {
    for (;;) {
        try {
            int state = runState;
            if (state > SHUTDOWN)
                return null;
            Runnable r;
            if (state == SHUTDOWN)  // Help drain queue
                r = workQueue.poll();
            else if (poolSize > corePoolSize || allowCoreThreadTimeOut) //如果線程數(shù)大于核心池大小或者允許為核心池線程設(shè)置空閑時間膘格,
                //則通過poll取任務(wù)瘪贱,若等待一定的時間取不到任務(wù)政敢,則返回null
                r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);
            else
                r = workQueue.take();
            if (r != null)
                return r;
            if (workerCanExit()) {    //如果沒取到任務(wù)喷户,即r為null褪尝,則判斷當前的worker是否可以退出
                if (runState >= SHUTDOWN) // Wake up others
                    interruptIdleWorkers();   //中斷處于空閑狀態(tài)的worker
                return null;
            }
            // Else retry
        } catch (InterruptedException ie) {
            // On interruption, re-check runState
        }
    }
}

在getTask中河哑,先判斷當前線程池狀態(tài)璃谨,如果runState大于SHUTDOWN(即為STOP或者TERMINATED)鲤妥,則直接返回null棉安。

如果runState為SHUTDOWN或者RUNNING贡耽,則從任務(wù)緩存隊列取任務(wù)鹊汛。

如果當前線程池的線程數(shù)大于核心池大小corePoolSize或者允許為核心池中的線程設(shè)置空閑存活時間刁憋,則調(diào)用poll(time,timeUnit)來取任務(wù)职祷,這個方法會等待一定的時間届囚,如果取不到任務(wù)就返回null意系。

然后判斷取到的任務(wù)r是否為null蛔添,為null則通過調(diào)用workerCanExit()方法來判斷當前worker是否可以退出迎瞧,我們看一下workerCanExit()的實現(xiàn):

private boolean workerCanExit() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    boolean canExit;
    //如果runState大于等于STOP凶硅,或者任務(wù)緩存隊列為空了
    //或者  允許為核心池線程設(shè)置空閑存活時間并且線程池中的線程數(shù)目大于1
    try {
        canExit = runState >= STOP ||
            workQueue.isEmpty() ||
            (allowCoreThreadTimeOut &&
             poolSize > Math.max(1, corePoolSize));
    } finally {
        mainLock.unlock();
    }
    return canExit;
}

也就是說如果線程池處于STOP狀態(tài)足绅、或者任務(wù)隊列已為空或者允許為核心池線程設(shè)置空閑存活時間并且線程數(shù)大于1時氢妈,允許worker退出首量。如果允許worker退出加缘,則調(diào)用interruptIdleWorkers()中斷處于空閑狀態(tài)的worker琅捏,我們看一下interruptIdleWorkers()的實現(xiàn):

void interruptIdleWorkers() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (Worker w : workers)  //實際上調(diào)用的是worker的interruptIfIdle()方法
            w.interruptIfIdle();
    } finally {
        mainLock.unlock();
    }
}

從實現(xiàn)可以看出柄延,它實際上調(diào)用的是worker的interruptIfIdle()方法,在worker的interruptIfIdle()方法中:

void interruptIfIdle() {
    final ReentrantLock runLock = this.runLock;
    if (runLock.tryLock()) {    //注意這里市俊,是調(diào)用tryLock()來獲取鎖的摆昧,因為如果當前worker正在執(zhí)行任務(wù)绅你,鎖已經(jīng)被獲取了忌锯,是無法獲取到鎖的
                                //如果成功獲取了鎖偶垮,說明當前worker處于空閑狀態(tài)
        try {
    if (thread != Thread.currentThread())  
    thread.interrupt();
        } finally {
            runLock.unlock();
        }
    }
}

這里有一個非常巧妙的設(shè)計方式似舵,假如我們來設(shè)計線程池,可能會有一個任務(wù)分派線程龙助,當發(fā)現(xiàn)有線程空閑時泌参,就從任務(wù)緩存隊列中取一個任務(wù)交給空閑線程執(zhí)行沽一。但是在這里漓糙,并沒有采用這樣的方式昆禽,因為這樣會要額外地對任務(wù)分派線程進行管理,無形地會增加難度和復(fù)雜度捡硅,這里直接讓執(zhí)行完任務(wù)的線程去任務(wù)緩存隊列里面取任務(wù)來執(zhí)行壮韭。

我們再看addIfUnderMaximumPoolSize方法的實現(xiàn),這個方法的實現(xiàn)思想和addIfUnderCorePoolSize方法的實現(xiàn)思想非常相似琳拨,唯一的區(qū)別在于addIfUnderMaximumPoolSize方法是在線程池中的線程數(shù)達到了核心池大小并且往任務(wù)隊列中添加任務(wù)失敗的情況下執(zhí)行的:

private boolean addIfUnderMaximumPoolSize(Runnable firstTask) {
    Thread t = null;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        if (poolSize < maximumPoolSize && runState == RUNNING)
            t = addThread(firstTask);
    } finally {
        mainLock.unlock();
    }
    if (t == null)
        return false;
    t.start();
    return true;
}

看到?jīng)]有狱庇,其實它和addIfUnderCorePoolSize方法的實現(xiàn)基本一模一樣恶耽,只是if語句判斷條件中的poolSize < maximumPoolSize不同而已驳棱。

到這里社搅,大部分朋友應(yīng)該對任務(wù)提交給線程池之后到被執(zhí)行的整個過程有了一個基本的了解形葬,下面總結(jié)一下:

1)首先暮的,要清楚corePoolSize和maximumPoolSize的含義冻辩;

2)其次恨闪,要知道Worker是用來起到什么作用的;

3)要知道任務(wù)提交給線程池之后的處理策略老玛,這里總結(jié)一下主要有4點:

如果當前線程池中的線程數(shù)目小于corePoolSize蜡豹,則每來一個任務(wù)镜廉,就會創(chuàng)建一個線程去執(zhí)行這個任務(wù)愚战;
如果當前線程池中的線程數(shù)目>=corePoolSize,則每來一個任務(wù)视乐,會嘗試將其添加到任務(wù)緩存隊列當中佑淀,若添加成功,則該任務(wù)會等待空閑線程將其取出去執(zhí)行谎砾;若添加失斁巴肌(一般來說是任務(wù)緩存隊列已滿)挚币,則會嘗試創(chuàng)建新的線程去執(zhí)行這個任務(wù)妆毕;
如果當前線程池中的線程數(shù)目達到maximumPoolSize笛粘,則會采取任務(wù)拒絕策略進行處理湿硝;
如果線程池中的線程數(shù)量大于 corePoolSize時关斜,如果某線程空閑時間超過keepAliveTime蚤吹,線程將被終止,直至線程池中的線程數(shù)目不大于corePoolSize裁着;如果允許為核心池中的線程設(shè)置存活時間扔罪,那么核心池中的線程空閑時間超過keepAliveTime桶雀,線程也會被終止。

3.線程池中的線程初始化

默認情況下敞咧,創(chuàng)建線程池之后辜腺,線程池中是沒有線程的评疗,需要提交任務(wù)之后才會創(chuàng)建線程百匆。
在實際中如果需要線程池創(chuàng)建之后立即創(chuàng)建線程,可以通過以下兩個方法辦到:

  • prestartCoreThread():初始化一個核心線程存璃;
  • prestartAllCoreThreads():初始化所有核心線程

下面是這2個方法的實現(xiàn):

public boolean prestartCoreThread() {
    return addIfUnderCorePoolSize(null); //注意傳進去的參數(shù)是null
}

public int prestartAllCoreThreads() {
    int n = 0;
    while (addIfUnderCorePoolSize(null))//注意傳進去的參數(shù)是null
        ++n;
    return n;
}

注意上面?zhèn)鬟M去的參數(shù)是null,根據(jù)第2小節(jié)的分析可知如果傳進去的參數(shù)為null,則最后執(zhí)行線程會阻塞在getTask方法中的

r = workQueue.take();

即等待任務(wù)隊列中有任務(wù)男图。

4.任務(wù)緩存隊列及排隊策略

在前面我們多次提到了任務(wù)緩存隊列逊笆,即workQueue难裆,它用來存放等待執(zhí)行的任務(wù)褂痰。

workQueue的類型為BlockingQueue<Runnable>症虑,通车荆可以取下面三種類型:

1)ArrayBlockingQueue:基于數(shù)組的先進先出隊列,此隊列創(chuàng)建時必須指定大泄淝颉颤绕;

2)LinkedBlockingQueue:基于鏈表的先進先出隊列,如果創(chuàng)建時沒有指定此隊列大小涕烧,則默認為Integer.MAX_VALUE议纯;

3)synchronousQueue:這個隊列比較特殊瞻凤,它不會保存提交的任務(wù)阀参,而是將直接新建一個線程來執(zhí)行新來的任務(wù)蛛壳。

5.任務(wù)拒絕策略###

當線程池的任務(wù)緩存隊列已滿并且線程池中的線程數(shù)目達到maximumPoolSize衙荐,如果還有任務(wù)到來就會采取任務(wù)拒絕策略忧吟,通常有以下四種策略:

ThreadPoolExecutor.AbortPolicy:丟棄任務(wù)并拋出RejectedExecutionException異常。
ThreadPoolExecutor.DiscardPolicy:也是丟棄任務(wù)斩披,但是不拋出異常溜族。
ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務(wù),然后重新嘗試執(zhí)行任務(wù)(重復(fù)此過程)
ThreadPoolExecutor.CallerRunsPolicy:由調(diào)用線程處理該任務(wù)

6.線程池的關(guān)閉

ThreadPoolExecutor提供了兩個方法垦沉,用于線程池的關(guān)閉煌抒,分別是shutdown()和shutdownNow(),其中:

  • shutdown():不會立即終止線程池乡话,而是要等所有任務(wù)緩存隊列中的任務(wù)都執(zhí)行完后才終止摧玫,但再也不會接受新的任務(wù)

  • shutdownNow():立即終止線程池,并嘗試打斷正在執(zhí)行的任務(wù),并且清空任務(wù)緩存隊列诬像,返回尚未執(zhí)行的任務(wù)

7.線程池容量的動態(tài)調(diào)整

ThreadPoolExecutor提供了動態(tài)調(diào)整線程池容量大小的方法:setCorePoolSize()和setMaximumPoolSize(),

setCorePoolSize:設(shè)置核心池大小
setMaximumPoolSize:設(shè)置線程池最大能創(chuàng)建的線程數(shù)目大小
當上述參數(shù)從小變大時,ThreadPoolExecutor進行線程賦值,還可能立即創(chuàng)建新的線程來執(zhí)行任務(wù)否纬。

三.使用示例##

四.如何合理配置線程池的大小##

本節(jié)來討論一個比較重要的話題:如何合理配置線程池大小烙心,僅供參考匙瘪。

一般需要根據(jù)任務(wù)的類型來配置線程池大小:

如果是CPU密集型任務(wù),就需要盡量壓榨CPU,參考值可以設(shè)為 NCPU+1

如果是IO密集型任務(wù),參考值可以設(shè)置為2*NCPU

當然,這只是一個參考值袒哥,具體的設(shè)置還需要根據(jù)實際情況進行調(diào)整,比如可以先將線程池大小設(shè)置為參考值,再觀察任務(wù)運行情況和系統(tǒng)負載、資源利用率來進行適當調(diào)整。

參考來自http://blog.csdn.net/he90227/article/details/52576452

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末稽穆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌否灾,老刑警劉巖扣汪,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舞痰,死亡現(xiàn)場離奇詭異学搜,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門暮屡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來莺掠,“玉大人,你說我怎么就攤上這事〔齑浚” “怎么了具则?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵膊畴,是天一觀的道長。 經(jīng)常有香客問我,道長讽挟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任贡歧,我火速辦了婚禮律想,結(jié)果婚禮上身笤,老公的妹妹穿的比我還像新娘涡尘。我一直安慰自己川梅,他們只是感情好丢早,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著惯驼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪俯萎。 梳的紋絲不亂的頭發(fā)上熊榛,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天园欣,我揣著相機與錄音,去河邊找鬼哪轿。 笑死宣脉,一個胖子當著我的面吹牛塑猖,可吹牛的內(nèi)容都是我干的吹菱。 我是一名探鬼主播输瓜,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼奶镶,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了陪拘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤纤壁,失蹤者是張志新(化名)和其女友劉穎左刽,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體酌媒,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡欠痴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了秒咨。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喇辽。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖雨席,靈堂內(nèi)的尸體忽然破棺而出菩咨,到底是詐尸還是另有隱情,我是刑警寧澤陡厘,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布抽米,位于F島的核電站,受9級特大地震影響糙置,放射性物質(zhì)發(fā)生泄漏云茸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一谤饭、第九天 我趴在偏房一處隱蔽的房頂上張望标捺。 院中可真熱鬧,春花似錦揉抵、人聲如沸亡容。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽萍倡。三九已至,卻和暖如春辟汰,著一層夾襖步出監(jiān)牢的瞬間列敲,已是汗流浹背阱佛。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留戴而,地道東北人凑术。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像所意,于是被迫代替她去往敵國和親淮逊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

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