线程池的源码简单梳理

文章目录

  • 前言
  • 正文
    • 核心函数 execute(Runnable command)
    • 新建线程
    • 在子线程中执行任务
    • 收尾工作
  • 总结

前言

在看 Okhttp 源码的时候,看到有关线程池的使用,先看看调用链:

OkHttp 框架的简单使用代码示例:

// 代码段0
// OkHttpActivity.java
        // 第一,新建客户端
        OkHttpClient okHttpClient = new OkHttpClient();

        /// 第二,构建请求
        final Request request = new Request.Builder()
                .url(url) // 想该 url 发送请求
                .get() //默认就是GET请求,可以不写
                .build();
                
        // 第三,新建Call
        Call call = okHttpClient.newCall(request);
        
        // 第四,发起调用
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.d(TAG, "onFailure: ");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.d(TAG, "onResponse: " + response.body().string());
            }
        });

看第四点,进入源码,
goto: call.enqueue(new Callback(){...})

// 代码段5
// RealCall.java

  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

goto: client.dispatcher().enqueue(new AsyncCall(responseCallback))

// 代码段6
// okhttp-3.11.0-sources.jar!/okhttp3/
// Dispatcher.java

  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

goto executorService()

// 代码段7
// okhttp-3.11.0-sources.jar!/okhttp3/
// Dispatcher.java

  public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

由此可知,OkHttp 框架使用了可缓存线程池:CachedThreadPool。
为什么使用这种线程池?这种线程池有什么特点?
先说结论,再去看原理。

// 各个参数的含义:

new ThreadPoolExecutor(
0,                 // 核心线程数,int corePoolSize
Integer.MAX_VALUE, // 最大线程数,int maximumPoolSize
60,                // 非核心线程的闲置超时时间,long keepAliveTime
TimeUnit.SECONDS,  // 时间单位(配合上一个参数),TimeUnit unit
new SynchronousQueue(),              // 阻塞队列 BlockingQueue
Util.threadFactory("OkHttp Dispatcher", false) // 线程工厂 ThreadFactory,用来给线程设置名字,一般可省略。
)

对于线程池的理解,可以简单的看看 刘望舒,线程池

为什么使用 CachedThreadPool,引用 刘望舒,线程池 中的一段:

CachedThreadPool在程序执行时会创建与所需数量相同的线程,然后在它回收旧线程时停止创建新线程,因此是Executor的首选,只有当这种方式会引发问题,或者不符合业务需要时才采用另外的三种Executor提供的线程池

正文

核心函数 execute(Runnable command)

接着代码段6,
goto executorService().execute(call);

// 代码段8
// Android/sdk/sources/android-28/java/util/concurrent/
// ThreadPoolExecutor.java

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) { //8-1
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) { //8-2
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0) //8-2.5
                addWorker(null, false);
        }
        else if (!addWorker(command, false)) //8-3
            reject(command); //8-4
    }

8-1处,判断如果 「当前工作的线程数」 < 「核心线程数」,

通过 addWorker(command, true) 立马新建线程去执行任务,然后 return;

⚠️ addWorker 函数的第一个参数 command,表示直接执行该任务,不需要从阻塞队列中取;

⚠️ addWorker 函数的第二个参数为 true,表示受「核心线程数」的限制,即增加核心线程去处理任务。


8-2处,执行到此处,说明 「当前工作的线程数」 >= 「核心线程数」;
这时候,先通过 `workQueue.offer(command)` 让任务入队;
入队成功后,等待「**未来某个线程进入空闲状态**」就来处理它即可;
但以防万一,,在 8-2.5 处再执行一次判断,若当前工作的线程数 == 0,则通过 `addWorker(null, false)` 建线程,保证至少有一个线程,最终会执行到刚入队的任务。
⚠️ addWorker 函数的第一个参数为 null,表示要从阻塞队列中取任务;
⚠️ addWorker 函数的第二个参数为 false,表示受「最大线程数」的限制,即增加非核心线程去处理任务。

8-3处,若执行到此处,说明任务入队失败,「阻塞队列已满」;
此时的情况是:「当前工作的线程数」 >= 「核心线程数」,而且 「阻塞队列已满」;
这时候会再通过 `addWorker(command, false)` 增加非核心线程去处理当前 command 这个任务。

8-4处,若执行到此处,说明 8-3处 增加非核心线程失败; 进一步可知,「当前工作的线程数」>= 「最大线程数」,也有可能是线程池不是「RUNNING状态」; 最后通过 `reject(command)` 语句,来拒绝该任务。
**_上述逻辑就是线程池的核心逻辑,其他成员函数和成员变量都是为该逻辑服务的。_**

引用该链接 深入理解Java线程池:ThreadPoolExecutor 的一段总结:

简单来说,在执行execute()方法时如果状态一直是RUNNING时,的执行过程如下:

1,如果workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务;

2,如果workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中;

3,如果workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务;

4,如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。

引用该链接 深入理解Java线程池:ThreadPoolExecutor 的一张图来解析 execute(Runnable command) 函数:

线程池的源码简单梳理_第1张图片


**再来一个不严谨的简单概括:** 假设:corePoolSize = 10,maximumPoolSize = 1000;
在这种情况下,一般线程池最多只派出 10 个线程,来处理任务,这 10 个线程也叫「核心线程」;
所以「核心线程」的另一个含义就是可以一直存在。

而对于来不及处理任务,就排队等待;

如果队列满了,又再来任务,才会在「核心线程」之外新建「非核心线程」,专门来处理排不上队的任务;

并且,「核心线程」数 +「非核心线程」数 即 = 线程总数 <= maximumPoolSize。

只让 10 个「核心线程」一直存在的原因是节省内存空间,节省资源;有些任务所需的处理时间很短,排一下队,让 10 个「核心线程」逐个处理就好了,不会耽误很长时间;否则如果同时进来一万个任务,就开一万个线程,那将会是非常浪费内存和资源的。

打个形象的比方,银行窗口(线程)就开那么几个,但却一堆人(任务)在排队办业务。

新建线程

接着看 addWorker 函数的内部如何实现:

goto addWorker(command, true)addWorker(null, false)addWorker(command, false)

// 代码段9
// ThreadPoolExecutor.java

    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;
                // 尝试让线程数加1
                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 {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int rs = runStateOf(ctl.get());

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

compareAndIncrementWorkerCount(c) 该语句,让 c 加 1,即让线程数加 1;
t.start() 该语句,启动 Worker 实例中的 线程;

接着看 Worker 类的源码:

goto class Worker

// 代码段10
// ThreadPoolExecutor.java

    private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
        ......
        
        // 创建时会同步创建一个线程
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

        /** Delegates main run loop to outer runWorker. */
        public void run() {
            runWorker(this);
        }
        
        ......
    }

可以看到,this.thread = getThreadFactory().newThread(this);

该语句,在创建 Worker 实例时,其构造函数同时创建一个线程,并把引用交给自身的成员变量 thread ,并将 Worker 实例自身 this 传给 thread,所以,只要执行该 thread 的 start() 函数,即可进入子线程,在子线程中执行到该 Worker 实例的 run 函数,进而调用 runWorker(this)。

在子线程中执行任务

goto runWorker(this)

// 代码段11
// ThreadPoolExecutor.java

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            //11-1 在此处调用了 getTask() !
            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(); //11-2 执行了任务
                    } 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);
        }
    }

注意,该函数的执行是在子线程中,即 Worker 实例的成员变量 thread 所引用的线程中!

先关注11-1处,该语句 while (task != null || (task = getTask()) != null)

满足第一个条件 task != null ,则不会执行第二个条件的语句 (task = getTask()) != null

若满足第一个条件,意味着是从 代码段8 的 8-1 addWorker(command, true) 或 8-3 addWorker(command, false) 的语句过来的;

否则,就是从 代码段8 的 8-2.5 addWorker(null, false) 的语句过来的。

接着可以看到11-2处,task.run(); 该语句,执行了开发者提交给线程池的任务!

其中,getTask() 这是获取任务的作用。

最终,要走这里 processWorkerExit(w, completedAbruptly)

先看 getTask() 函数:

goto getTask()

// 代码段12
// ThreadPoolExecutor.java

    private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);

            // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

这两处就是从队列取任务的:

workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)
workQueue.take()

整个 线程池 类,就只有此处有取任务的代码。

收尾工作

再回到代码段13,finally 会走 processWorkerExit 函数。

goto processWorkerExit(w, completedAbruptly)

// 代码段13
// ThreadPoolExecutor.java

    private void processWorkerExit(Worker w, boolean completedAbruptly) {
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            completedTaskCount += w.completedTasks;
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }

        tryTerminate();

        int c = ctl.get();
        if (runStateLessThan(c, STOP)) { //13-1
            if (!completedAbruptly) {
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            addWorker(null, false);
        }
    }

if (runStateLessThan(c, STOP)),表示线程池没有 STOP

那就会通过 addWorker(null, false) 来新增线程,取队列的任务执行。

到此可知,线程是不断回收和新建的,只是数量一只保持在「核心线程」数之内,

这就产生了一直有固定几个线程实例在运行的错觉。

总结

本文不严谨的只关注到其中的一些主要逻辑,忽略了很多细节;

更详细的内容可以参考该文章,非常详细,可多读几遍:

深入理解Java线程池:ThreadPoolExecutor

关于一些状态量和位运算,可以参考:

Java并发之线程池ThreadPoolExecutor源码分析学习

最后对重要函数做一个总结:

  • execute(Runnable command)
    • 决定进来的任务:立刻由核心员工(线程)接待,还是排队等待
    • 若排不上队,再由非核心员工(线程)接待
    • 若员工(线程)总数超标,则拒绝任务
  • addWorker(Runnable firstTask, boolean core)
    • 检查相关条件
    • 新建线程,并更新相关变量
    • 启动线程
  • runWorker(Worker w)
    • 该步骤起,进入 子线程执行
    • 接手当前任务,或者从队列取任务 getTask()
    • 执行任务
  • processWorkerExit(Worker w, boolean completedAbruptly)
    • 执行完任务的收尾工作
    • 保证还有一定数量的线程去处理队列的任务
    • 若线程不足就新增线程,即 addWorker(null, false)

你可能感兴趣的:(android)