AsyncTask必知必会(二)

来源:互联网 发布:知乎 卢煜明 编辑:程序博客网 时间:2024/06/04 18:44

AsyncTask必知必会(二)

转载请注明出处: http://blog.csdn.net/qinjunni2014/article/details/43958245

在上一讲中,我们谈到,Asynctask.ThreadPoolExecutor在调度任务执行的时候,遵从的顺序是

  1. 先安排core_pool_size数量的任务以core thread执行,
  2. 然后将多余任务入工作队列,
  3. 如果队列也满,则将多余任务以非core thread运行直到,
  4. 运行的线程数量达到maximum_pool_size,则调用rejectHandler拒绝任务

接一下,我们首先从源码的角度分析这一过程的实现:

我们进入ThreadPoolExecutor.java类,定位到execute(Runnable command)函数,

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

代码中已经给出execute所走的流程的注释。大家一眼就非常明了了。我们先分析如下几个函数:

addWorker

首先贴出这个函数的代码:

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

addWorker函数有两个参数,第一个是要执行的任务,是一个Runnable对象,第二个参数是一个bool变量,代表是否正在添加core任务,

为了弄清楚addWorkder的流程,首先必须弄清楚几个变量:

ctl与c

ctl的类型是AtomicInteger, AtomicInteger的api解释为:

An int value that may be updated atomically. See the java.util.concurrent.atomic package specification for description of the properties of atomic variables. An AtomicInteger is used in applications such as atomically incremented counters, and cannot be used as a replacement for an Integer. However, this class does extend Number to allow uniform access by tools and utilities that deal with numerically-based classes.

简单的说就是一个提供原子操作的Integer类,类似的类型还有AtomicBoolean, AtomicLong 等等,在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字。而AtomicInteger则通过一种线程安全的加减操作接口。它提供一系列get、set、increment、decrement、compare等线程安全的方法,因此用一个AtomicInteger类型的ctl就可以保证在多线程环境下,任务运行状态的稳定。

rs 与 wc

rs代表当前运行状态,wc代表正在运行的工作线程数量,他们的获取也颇为简单:

int c = ctl.get();int rs = runStateOf(c);int wc = workerCountOf(c);private static final int COUNT_BITS = Integer.SIZE - 3;private static final int CAPACITY   = (1 << COUNT_BITS) - 1;private static int runStateOf(int c)     { return c & ~CAPACITY; }private static int workerCountOf(int c)  { return c & CAPACITY; }private static int ctlOf(int rs, int wc) { return rs | wc; }

c就是rs 与 wc与运算的结果,rs为c的高4位,wc为c的低28位
除此之外,需要注意的是ThreadPoolExecutor 有RUNNING, SHUTDOWN, STOP, TIDYING, TERMINATED 五种状态,从左至右数值越来越大,有两种状态变迁模式:

  1. RUNNING –>SHUTDONW –>TIDYING –>TERMINATED
  2. RUNNING –>STOP –>TIDYING –>TERMINATED

我们再回过头来看addworker的实现:
先来分析retry部分代码,大家请看注释:

retry:for (;;) {    //拿到ctl,与运行状态    int c = ctl.get();    int rs = runStateOf(c);    // Check if queue empty only if necessary.    if (rs >= SHUTDOWN &&! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()))        //这个if判断稍许复杂,左边为rs不为running, 右边的判断实际上为rs != shutdown || firstTask != null || workQueue.isEmpty(),        //因此只有在rs 为running或者 [shutdown 而且 firsttask不为null, workQueue不为空]的情况下,可以添加新的工作线程,否则直接返回        return false;    for (;;) {        //获取当前工作的线程数量        int wc = workerCountOf(c);        //判断wc是否已经超过capacity 或者 pool size        if (wc >= CAPACITY ||            wc >= (core ? corePoolSize : maximumPoolSize))            return false;        //更新c,将workCount + 1, (先比较 在更新)更新之后跳出外循环:        if (compareAndIncrementWorkerCount(c))            break retry;        //如果更新失败,拿到c        c = ctl.get();  // Re-read ctl        //如果失败是因为runstate不对 继续外循环        if (runStateOf(c) != rs)            continue retry;        //如果失败是因为workcount不对,继续内部循环        // else CAS failed due to workerCount change; retry inner loop    }}

下面部分为实际添加工作线程的部分:

//返回的标记boolean workerStarted = false;boolean workerAdded = false;Worker w = null;try {    //创建新的worker    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());            //重新检查标记,只有在runstate为running或者 为shutdown 而且 firstTask为null时 添加新的工作线程            if (rs < SHUTDOWN ||                (rs == SHUTDOWN && firstTask == null)) {                if (t.isAlive()) // precheck that t is startable                    throw new IllegalThreadStateException();                //添加到workers set中                workers.add(w);                int s = workers.size();                if (s > largestPoolSize)                    //更新largestPoolSize                    largestPoolSize = s;                workerAdded = true;            }        } finally {            //解锁            mainLock.unlock();        }        if (workerAdded) {            //启动线程            t.start();            //设置已经启动标记            workerStarted = true;        }    }} finally {    if (! workerStarted)        //在此减小之前增加的workerCount 数目        addWorkerFailed(w);}return workerStarted;

总的来说addworker函数就干了两件事:一件事是更新当前的workcount数目,另一件事是创建一个新的worker并启动其内部线程。

加入队列

分析完addWorker,execute第一部分就相当明了了,即在workercount < core_pool_size时,添加新的工作线程,我们再来分析第二部分代码:

if (isRunning(c) && workQueue.offer(command)) {//如果处于运行状态 而且command成功添加到workQueue中后    int recheck = ctl.get();    /*重新检查状态,如果runstate不为running,则从workQueue中移除command,移除成功后,调用reject 拒绝command的执行*/    if (! isRunning(recheck) && remove(command))        reject(command);    else if (workerCountOf(recheck) == 0)        addWorker(null, false);}

添加非core工作

最后一部分代码即,在加入workQueue失败后,尝试添加非core工作线程,如果添加失败,直接拒绝command的执行.

Core线程和非Core线程的区别

默认情况下,非core线程如果任务执行完毕之后,会等待一段时间,如果没有新的任务到达,线程会结束,这个等待的时间就是keepAliveTime, core线程启动后就会一直运行,除非调用allowCoreThreadTimeOut(true),

Sets the policy governing whether core threads may time out and terminate if no tasks arrive within the keep-alive time, being replaced if needed when new tasks arrive. When false, core threads are never terminated due to lack of incoming tasks. When true, the same keep-alive policy applying to non-core threads applies also to core threads. To avoid continual thread replacement, the keep-alive time must be greater than zero when setting true. This method should in general be called before the pool is actively used.

此时core线程也会应用和非core线程一样的策略,在没有任务到达时,等待指定时间后 结束。

RejectedExceptionHandler

前文说到,如果当前工作的线程数量已经达到maximum_pool_size个,还有任务到达时,我们需要一个handler来处理多余的任务,RejectedExecutionHandler是一个接口,需要我们传入自己的继承类对象,系统已经实现了四个RejectedExceptionHandler继承类,分别是:

  1. AbortPolicy,抛出RejectedExecutionException异常,
  2. DiscardPolicy 不做任何处理,相当于忽略掉任务
  3. DiscardOldestPolicy 从工作队列中取出最前面的任务,再重新调用execute(r)
  4. CallerRunsPolicy 直接在当前调用线程中运行任务。

如果我们在构造ThreadPoolExecutor时不手动提供handler, 则系统默认帮我们传入一个AbortPolicy的对象。

我们也可以自定义自己的RejectExceptionHandler, 只需要实现rejectedExecution(Runnable r, ThreadPoolExecutor e)函数即可

ThreadFactory

构造ThreadPoolExecutor时,我们还需要传入一个ThreadFactory对象,用于创建线程,默认参数为DefaultThreadFactory类,该工厂在创建线程时,会指定它所属的组,分配一个”pool-“+序号+”thread”的名称前缀,并且将线程设为非daemon,优先级设为NORM_PRIORITY。

至此,AsyncTask的ThreadPoolExecutor实现方式,我们就理解完毕,我们理解了asyncTask任务运行的机制,我们就知道了如何去高效的使用它。

0 0
原创粉丝点击