安卓开发学习之线程池源码简析

背景

在进行安卓开发的时候,避免不了要用线程,而谷歌推荐我们使用线程池来进行线程的管理,所以有必要对线程池进行源码的阅读


使用

线程池的一般使用如下

    private static final int core_number = Runtime.getRuntime().availableProcessors(); // 线程池里最小保活worker线程数,采用cpu数量
    private static final int keep_alive_time = 3; // 每个worker线程最大运行时间
    private static final TimeUnit timeUnit = TimeUnit.SECONDS; // 时间单位
    private static final BlockingQueue<Runnable> taskQueue = new LinkedBlockingDeque<>(); // 线程池内部的任务线程队列
    private static final ExecutorService executor = new ThreadPoolExecutor(core_number, core_number * 2,
            keep_alive_time, timeUnit, taskQueue, Executors.defaultThreadFactory()); // 实例化线程池

    public static void executeRunnable(Runnable runnable) {
        executor.execute(runnable); // 执行自定义线程
    }

一切都是从executor.execute(runnable)开始的,对源码的阅读也就从这个方法开始


源码阅读

ThreadPoolExecutor.execute(runnable)源码如下

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException(); // 如果传入的线程是null,直接抛异常
        int c = ctl.get(); // 当前worker数量,worker是线程池内部的一个类,用来执行线程队列里的线程
        if (workerCountOf(c) < corePoolSize) {
            // worker数小于线程池中要保活的最小worker线程数,先尝试直接启动线程
            if (addWorker(command, true)) // addWorker()返回true,表示已经执行了线程
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            // offer()方法用来在队尾添加元素,如若队满,则返回false
            int recheck = ctl.get(); // 再次检查计数器
            if (! isRunning(recheck) && remove(command))
                reject(command); // 如果线程池已满并且可以从队列中移除runnable,就抛出异常
            else if (workerCountOf(recheck) == 0) // 如果线程池空了,就添加一个默认的worker,但这个worker的初始线程是null
                addWorker(null, false);
        }
        // 在计数器的值已经大于保活数,并且任务队列已满的情况下,再加不进去新线程command,就直接拒绝command
        else if (!addWorker(command, false))
            reject(command);
    }

ctl是原子整数(AtomicInteger),默认值是0,以提供线程安全的计数功能。

worker是线程池内部封装的一个类,也是一个线程,用来进行任务线程的调度执行,当执行线程的run()方法时,会先执行worker的run()方法,线程的run()会在worker的run()里执行。线程池里,多个worker共享一个线程队列。每新传入一个线程,worker集合会add一个新建出来的worker,而当一个worker从任务线程队列中获取到一个任务线程(获取不到就不执行,直接移除)并执行完后,worker集合会将此worker移除出去,并以一个空的worker替换之。

corePoolSize就是我们在构造方法里传入的最小保活worker线程数,当worker数量小于这个数目时,worker从线程队列取线程时,是要限时的,限时时长就是我们在构造方法中传入的keep_alive_time

可见,核心方法有三个:addWorker()、worker.run()、reject()

线程池里直接操作的线程都是worker线程,worker则直接调度任务线程

addWorker()

代码如下

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

            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                // 状态检测和判空,状态不对直接返回false
                return false;

            // 这个无限循环用来尝试让worker计数器ctl+1,成功就退出retry循环
            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false; // worker数量已经满了,直接返回false
                if (compareAndIncrementWorkerCount(c))
                    // 尝试让当前计数器ctl+1,成功的话,退出retry循环
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    // 状态变化,进行下一次retry迭代
                    continue retry;
            }
        }

        // 到这一步,已经把worker计数器ctl++
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            // 添加worker,构造方法里会new一个线程,做为任务线程
            w = new Worker(firstTask);
            final Thread t = w.thread; // new出来的任务线程
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock(); // 获取锁
                try {
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException(); // 线程池关闭并且传入runnnable是空,但worker中的线程却可运行,状态就不对
                        workers.add(w); // 添加worker
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s; // 更新已分配的最大worker数
                        workerAdded = true; // 设置标志位
                    }
                } finally {
                    mainLock.unlock(); // 释放锁
                }
                if (workerAdded) {
                    // 启动任务线程,更新标志位
                    t.start(); // 这里会先执行worker的run()方法
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w); // 没有成功启动任务线程时的善后处理
        }
        return workerStarted;
    }

执行完retry循环后worker的计数器会++,然后会执行Worker的构造方法来新建一个worker,构造方法代码如下

        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask; // 保存传进来的线程任务
            this.thread = getThreadFactory().newThread(this); // worker自己也是一个runnable
        }

调用了ThreadFactory来构造线程,我们用的是DefaultThreadFactory,它的newThread()代码如下

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false); // 非守护线程
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY); // 普通优先级
            return t;
        }


worker#run()

回到ThreadPoolExecutor.addWorker()方法,新建完worker后,我们的runnable就会保存为worker的firstTask,在成功把新worker添加到worker集合中后,就会执行worker.thread.start()方法,这个方法其实就是执行worker本身的run()方法,代码如下

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

调用的是线程池的runWorker(),传入参数是worker自己

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask; // 获取firstTask,也就是我们的runnable
        w.firstTask = null;
        w.unlock(); // worker先解锁
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) { // firstTask可能是空,这个时候我们的runnable存储在任务队列中,getTask()方法会尝试从队列中获取线程
                w.lock(); // 锁住worker
                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 (Exception x) {
                       .. 异常
                    } finally {
                        afterExecute(task, thrown); // 空方法
                    }
                } finally {
                    task = null;
                    w.completedTasks++; // worker完成的任务数量+1
                    w.unlock(); // worker释放锁
                }
            }
            completedAbruptly = false; // 只有发生异常了,completedAbruptly才会是true
        } finally {
            processWorkerExit(w, completedAbruptly); // 无论执没执行线程任务,都会删除worker
        }
    }

看一下getTask()是怎么从线程队首获取任务的

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

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

            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount(); // 如果线程池关闭停止或任务队列是空,worker计数器-1,返回null
                return null;
            }

            int wc = workerCountOf(c);

            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; // worker数量大于最大保活worker线程数,就要进行对任务队列的限时出队

            if ((wc > maximumPoolSize || (timed && timedOut))
                    && (wc > 1 || workQueue.isEmpty())) { // worker数大于最大worker线程数,抑或是超时,结束当前迭代或直接返回空
                if (compareAndDecrementWorkerCount(c)) // 能够将worker计数器-1,返回null
                    return null;
                continue;
            }

            try {
                //如果corePool溢出或超时,就限时获取任务队列队首,否则不限时(如果任务队列是空,就阻塞在那儿了)
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r; // 如果能成功获取,就返回
                timedOut = true; // 否则,就是超时
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

可以看到,当线程队列是空时,worker线程是可以被阻塞的,它会一直阻塞到线程队列不是空或worker集合满

当获取到线程时,worker从getTask()返回,就会执行线程的run()方法,然后循环读取线程队列,不断进行处理或阻塞

当因线程池关掉、worker队列满、超时等原因而获取到的线程是null,就会退出处理循环,进入ThreadPoolExecutor.processWorkerExit()来清理这个worker

    private void processWorkerExit(Worker w, boolean completedAbruptly) {
        if (completedAbruptly) // 如果是因为抛出异常进来的这个方法,直接把worker数量-1
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            completedTaskCount += w.completedTasks; // 记录所有worker完成任务的数量
            workers.remove(w); // 从worker集合里去掉worker
        } finally {
            mainLock.unlock();
        }

        tryTerminate();

        int c = ctl.get();
        if (runStateLessThan(c, STOP)) { // 状态小于stop(shutdown或running)
            if (!completedAbruptly) {
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize; // 如果设置了allowCoreThreadTimeout,就把min设置成0,否则,就设成最小保活worker数
                if (min == 0 && ! workQueue.isEmpty()) // 如果allowCorewThreadTimeout,并且任务队列不是空,就把min改成1
                    min = 1;
                if (workerCountOf(c) >= min) // 如果当前worker数大于min,直接返回,不增加一个空的worker
                    return; // replacement not needed
            }
            addWorker(null, false); // 否则就要以一个空的worker代替老的worker
        }
    }

处理中间,调用了tryTerminate()方法,代码如下

    final void tryTerminate() {
        for (;;) {
            int c = ctl.get(); // worker数目
            if (isRunning(c) ||
                runStateAtLeast(c, TIDYING) ||
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
                return; // 状态检查
            if (workerCountOf(c) != 0) { 
                interruptIdleWorkers(ONLY_ONE); //  中断一个空闲worker线程
                return;
            }

            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { // 设置状态为tidying
                    try {
                        terminated(); // 空方法
                    } finally {
                        ctl.set(ctlOf(TERMINATED, 0)); // 更新状态为terminated
                        termination.signalAll();
                    }
                    return;
                }
            } finally {
                mainLock.unlock();
            }
            // else retry on failed CAS
        }
    }

如果当前线程池的状态>=tidying(tidying或terminated)或者正在运行或者已关闭但任务队列不是空,说明状态不对,直接返回。否则,如果worker队列不是空,就中断一个空闲的worker线程,然后返回。如果worker队列是空,就改变线程池状态为tidying

其中,中断一个空闲的worker线程调用了interruptIdleWorkers()方法,传入参数为true

    private void interruptIdleWorkers(boolean onlyOne) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers) {
                Thread t = w.thread;
                if (!t.isInterrupted() && w.tryLock()) {
                    try {
                        t.interrupt();
                    } catch (SecurityException ignore) {
                    } finally {
                        w.unlock();
                    }
                }
                if (onlyOne)
                    break;
            }
        } finally {
            mainLock.unlock();
        }
    }

就是遍历worker队列,找到一个没有被中断也没有运行的worker线程,由于传入参数onlyOne是true,所以找到一个符合条件的,中断完就返回了。

回到最初的execute(),如果线程池里worker数目已经超过了最小保活数,而且任务队列满,这时再次调用addWorker(),还是加不进去新线程的话,就调用reject()方法,表示添加线程失败


reject()

代码如下

    final void reject(Runnable command) {
        handler.rejectedExecution(command, this);
    }

调用的是ThreadPoolExecutor内部类AbortPolicy的rejectedExecution()方法,代码如下

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }

原来是直接抛出异常。

关于线程池的线程调度机制大致如此。接下来,我再看一看线程池里关于关闭的两个方法shutdown()和shutdownNow()的源代码,了解一下它的关闭机制


shutdown()

代码如下

    public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess(); // 检查是否允许关闭,但里面调用的securityManager.checkPermission()和checkAccess()都是空方法
            advanceRunState(SHUTDOWN); // 设置状态为shutdown
            interruptIdleWorkers(); // 中断所有空闲worker线程
            onShutdown(); // 空方法
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
    }

tryTerminate()上文已经说过,这里调用的作用应该就是设置一下线程池的状态是tidying。所以这里主要的方法就是interruptIdleWorkers(),此方法代码如下

    private void interruptIdleWorkers() {
        interruptIdleWorkers(false);
    }

调用了interruptIdleWorkers()方法,此方法上文已经说过,只不过这里传入的参数是false,也就是要中断所有的空闲worker线程


shutdownNow()

代码如下

    public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess(); // 空方法
            advanceRunState(STOP); // 设置状态是stop
            interruptWorkers(); // 中断所有worker线程
            tasks = drainQueue(); // 把任务队列清空并返回给外界
        } finally {
            mainLock.unlock();
        }
        tryTerminate(); 
        return tasks;
    }

和shutdown()不同的地方一个是shutdownNow()要把任务队列清空并返回给外界,导致线程池内直接没有了任务,再一个调用了interruptWorkers()方法,这个方法用来中断所有worker线程,不管空不空闲,此方法代码如下

    private void interruptWorkers() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers)
                w.interruptIfStarted();
        } finally {
            mainLock.unlock();
        }
    }

遍历调用了worker.interruptIfStarted(),只要线程start了,立马中断,代码如下

        void interruptIfStarted() {
            Thread t;
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
        }

只要线程启动(调用了runWorker()),getState()就>=0,其余不用多解释了,就是中断线程。


所以shutdown()只是中断空闲线程,并且shutdown状态会导致addWorker()直接返回false,也就是不会添加新的任务,但还是会执行完线程池内现有的任务。但shutdownNow()则是中断了所有worker线程,并且设置状态是stop,这种状态也会导致addWorker()不添加新任务,同时由于中断了所有worker线程,正在执行的任务也会被取消。


结语

线程池内部主要是用了worker集合和任务队列两个列表来管理线程,而worker本身就是一个线程,同时还有一个firstTask属性,也是一个线程,做为这个worker的默认任务,只有默认任务完了,worker才会在任务队列里获取任务来执行。

线程池里另有两个容量:最小保活worker数和最大worker数,在添加任务时,前者是worker集合的最大容量,后者是任务列表的最大容量。如果当前worker线程数小于最小保活worker数,那么直接把新任务做为新worker的firstTask,也就是新worker的默认任务,如果当前worker线程数大于等于最小保活worker数,并且小于最大worker数,说明此时worker集合已满但任务队列未满,所以就把新任务加到任务队列中,等待worker们调用执行。

worker的run()方法会一直尝试执行自身的默认任务或从任务队列中获取任务执行,如果自身没有任务,任务队列也是空,这个worker的run()方法就执行完了。

至于线程池的关闭,结论很简单,shutdown()不再接收新任务,但已有任务会被执行完,shutdownNow()则是不但不接收新任务,现有的任务也会全部被中断。


猜你喜欢

转载自blog.csdn.net/qq_37475168/article/details/80928049