java線程池中線程數量到底是幾

線程池配置

線程池配置,假設是:

1.最小數量是5

2.阻塞隊列容量是10

3.最大數量是20

線程池裡的業務線程數量小於最小數量(5)

第一個請求

第一個請求進來的時候,這個時候,線程池沒有線程,就創建新的工作線程(即Worker線程)。

然後,這個工作線程去處理當前請求的業務線程。

第二個請求

第二個請求進來的時候,這個時候,線程池已經有瞭一個工作線程。

但是,要註意,這個時候是不會復用線程池裡已有的工作線程的。而是創建新的工作線程。

因為,線程池裡根本沒有復用線程的概念。

說白瞭,無論線程池裡已有的這個工作線程是否在處理業務線程,即不管它空閑與否,其實都會創建新的工作線程。

第三個請求

同上,仍然創建新的工作線程。

。。。

第五個請求

同上。仍然創建新的工作線程。

註意,現在,線程池有幾個工作線程?5個。

即,每個請求進來,都創建一個新的工作線程。

小於阻塞隊列容量(10)

第六個請求

第六個請求進來的時候,提交到阻塞隊列。

然後,再慢慢消費。

具體來說,是由線程池裡的工作線程來慢慢消費。

具體消費的源碼,參考:復用線程小節。

第七個請求

同上,也是先添加到阻塞隊列。

。。。

第15個請求

同上,也是先添加到阻塞隊列。

小於最大數量(20)

第16個請求

先來看,正常情況下,阻塞隊列還沒塞滿(生產環境的容量一般是1000),就會被快速處理掉。

然後,當新的請求進來的時候,繼續丟到阻塞隊列裡面去。

這個是和上面講的一樣。

但是,我們為瞭方便理解,現在假設之前的15個請求是同時到達,即

  • 前面5個請求

創建5個新的請求。

  • 後面10個請求

全部丟到阻塞隊列。

這個時候,阻塞隊列已經滿瞭。接著,第16個請求進來瞭,怎麼辦?

繼續創建新的工作線程。

。。。

第35個請求

同上,繼續創建新的工作線程。

註意,這個時候,線程池裡的工作線程的數量是多少?20。

因為

  • 前面5個請求,創建瞭5個新的工作線程。
  • 最後面的15個請求(第16到第35),創建瞭15個新的工作線程。

所以,總共,創建瞭20個新的工作線程。線程池,總共有20個工作線程。

拒絕策略

第36個請求

假設前面的請求都沒有處理完,這個時候,來瞭第36個請求,怎麼辦?

隻能采取拒絕策略。

具體采用哪個拒絕策略?比如說,一般情況下,都是采用丟棄。

復用線程

前面說瞭,線程池裡沒有復用線程的概念。

那到底是怎麼回事呢?既然不能復用線程,那搞個線程池有個幾把用?

具體是這樣子,雖然,線程池裡的工作線程不能被復用,僅僅是指類似數據庫連接池裡的連接的那種復用,即

  • 用的時候,從連接池取
  • 用完瞭,歸還到連接池

線程池裡的對象復用,是基於循環,而不是用完之後再還回去。

什麼意思呢?就是工作線程,不斷的從阻塞隊列裡取業務線程,然後執行業務線程。

偽代碼

工作線程{
  run(){
    while(){
      1.從阻塞隊列,取業務線程
      2.執行業務線程;
    }
  }
}

所以,線程池和連接池的區別在於,線程池的對象是線程,可以不斷的循環讀業務線程。而連接池的對象,是用完瞭歸還到連接池裡去。

jdk源碼-java.util.concurrent.ThreadPoolExecutor#runWorker

/**
     * 核心步驟
     * 1.從阻塞隊列,讀業務線程
     * 2.執行業務線程
     * 
     * ---
     * Main worker run loop.  Repeatedly gets tasks from queue and
     * executes them, while coping with a number of issues:
     *
     * 1. We may start out with an initial task, in which case we
     * don't need to get the first one. Otherwise, as long as pool is
     * running, we get tasks from getTask. If it returns null then the
     * worker exits due to changed pool state or configuration
     * parameters.  Other exits result from exception throws in
     * external code, in which case completedAbruptly holds, which
     * usually leads processWorkerExit to replace this thread.
     *
     * 2. Before running any task, the lock is acquired to prevent
     * other pool interrupts while the task is executing, and then we
     * ensure that unless pool is stopping, this thread does not have
     * its interrupt set.
     *
     * 3. Each task run is preceded by a call to beforeExecute, which
     * might throw an exception, in which case we cause thread to die
     * (breaking loop with completedAbruptly true) without processing
     * the task.
     *
     * 4. Assuming beforeExecute completes normally, we run the task,
     * gathering any of its thrown exceptions to send to afterExecute.
     * We separately handle RuntimeException, Error (both of which the
     * specs guarantee that we trap) and arbitrary Throwables.
     * Because we cannot rethrow Throwables within Runnable.run, we
     * wrap them within Errors on the way out (to the thread's
     * UncaughtExceptionHandler).  Any thrown exception also
     * conservatively causes thread to die.
     *
     * 5. After task.run completes, we call afterExecute, which may
     * also throw an exception, which will also cause thread to
     * die. According to JLS Sec 14.20, this exception is the one that
     * will be in effect even if task.run throws.
     *
     * The net effect of the exception mechanics is that afterExecute
     * and the thread's UncaughtExceptionHandler have as accurate
     * information as we can provide about any problems encountered by
     * user code.
     *
     * @param w the worker
     */
    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            //從阻塞隊列裡獲取業務線程
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        //執行業務線程
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

到此這篇關於java線程池中線程數量到底是幾的文章就介紹到這瞭,更多相關java 線程數量內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: