Java源碼-線程池

一、線程池實現原理

Java支持多線程,多線程可以提高任務的執行效率。但是Java里面的線程跟操作系統的線程是一一對應的,所以創建過多的線程會對系統資源產生很大的消耗,同時過多的線程競爭CPU,也會產生頻繁的上下文切換,結果可能適得其反,降低系統的運行效率。
線程池的作用就是對線程的重復利用,把線程數量控制在合理的范圍內,避免上述情況的產生。
線程池有幾個部分組成:

  • 任務隊列:要執行的任務集合
  • 活躍的線程:用來執行任務的線程
  • 任務調度:控制任務執行

所以線程池的基本原理可以用下圖來表示:

簡易線程池原理.png

二、Java線程池類圖

線程池類圖.png
  • Executor : 定義任務提交執行execute,執行的線程可能是新的線程,線程池的線程,或者調用者的線程,取決于線程池的實現。
  • ExecutorService : 定義了有返回值的任務提交submit,線程池的中斷。
  • AbstractExecutorService : 實現submit方法
  • ScheduledExecutorService : 定義定時執行的方法
  • ThreadPoolExecutor : 線程池的主要實現類
  • ScheduledThreadPoolExecutor : 實現定時執行線程池的邏輯

三、ThreadPoolExecutor源碼分析

ThreadPoolExecutor是線程池的核心類,包含線程池的主要實現邏輯。

3.1、主要成員變量

  • AtomicInteger ctl :表示線程池當前的狀態(高3位)&有效線程數量(低29位)
  • BlockingQueue<Runnable> workQueue : 任務隊列
  • HashSet<Worker> workers : 活躍線程集合
  • int corePoolSize : 核心線程數
  • int maximumPoolSize : 最大線程數
  • RejectedExecutionHandler defaultHandler : 任務拒絕策略

3.2、execute

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

提交任務時,如果當前線程數量小于核心線程數,不管是否有線程空閑,都會新建一個線程來執行任務,當線程數量大于核心線程,小于最大線程時,只有當workQueue滿的時候,才會新建線程來執行,當線程數量等于最大線程數時,并且workQueue滿的時候,會執行任務拒絕策略。

addWorder

private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            final ReentrantLock mainLock = this.mainLock;
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int c = ctl.get();
                    int rs = runStateOf(c);

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

Worker封裝了執行任務的線程,它可以用一個任務初始化。新增一個worker首先判斷當前線程池的狀態還是否允許新增一個線程。如果可以,則會CAS更新workCount。然后new一個worker,調用worker線程的start方法。
worker線程是實現了Runnable的方法,worker線程會調用它的run方法,進而調用runWorker

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 ((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);
        }
    }

這個方法就會判斷一些狀態,然后調用Task的run方法,執行任務。

四、ScheduledThreadPoolExecutor源碼分析

ScheduledThreadPoolExecutor 繼承自ThreadPoolExecutor,本身自帶了線程池的能力,只需要實現定時執行功能即可。
定時執行的基本原理是存儲任務的隊列是一個優先級隊列,按執行時間的先后排序,任務線程去隊列獲取最近執行的任務,如果任務還沒有到執行時間,則等待直到可以執行。
ScheduledThreadPoolExecutor 會把要定時執行的任務封裝成一個ScheduledFutureTask。
ScheduledFutureTask 有幾個成員變量:

  • time 需要執行的時間
  • period 執行周期
  • sequenceNumber 序列號,如果時間相同,序列號小的先執行
    利用這三個參數,結合優先級隊列,可以實現定時執行。

DelayedWorkQueue.take

 public RunnableScheduledFuture take() throws InterruptedException {
            final ReentrantLock lock = this.lock;
            lock.lockInterruptibly();
            try {
                for (;;) {
                    RunnableScheduledFuture first = queue[0];
                    if (first == null)
                        available.await();
                    else {
                        long delay = first.getDelay(TimeUnit.NANOSECONDS);
                        if (delay <= 0)
                            return finishPoll(first);
                        else if (leader != null)
                            available.await();
                        else {
                            Thread thisThread = Thread.currentThread();
                            leader = thisThread;
                            try {
                                available.awaitNanos(delay);
                            } finally {
                                if (leader == thisThread)
                                    leader = null;
                            }
                        }
                    }
                }
            } finally {
                if (leader == null && queue[0] != null)
                    available.signal();
                lock.unlock();
            }
        }

先去獲取位于隊首的任務,如果為NULL則wait,然后看時間是否到達,如果到達了,返回任務,如果沒有,則等待現在距離任務執行之間的時間差。

四、線程池使用注意

  • 盡量使用 new ThreadPoolExecutor來構建線程池,這樣可以自己去控制和判斷線程池的大小。
  • 如果是CPU密集型,線程數量可以設置為CPU核數+1
  • 如果是IO密集型,線程數量可以設置為2*CPU核數+1
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容