线程池和AsyncTask源码分析

这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战

什么是线程池

线程池就是将线程进行池化,需要运行任务时从池中拿一个线程来执行,执行完毕,线程放回池中。

使用线程池的好处

  • 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗
  • 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  • 提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。

线程池的创建

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
}
复制代码

各个参数的含义:

  • corePoolSize:线程池中的核心线程数
  • maximumPoolSize:线程池中允许的最大线程数
  • keepAliveTime:线程空闲时的存活时间(只在线程数大于corePoolSize时才有用)
  • unit:keepAliveTime的时间单位
  • workQueue:阻塞队列.当线程池中的线程数超过它的corePoolSize的时候,线程会进入阻塞队列进行阻塞等待
  • threadFactory:创建线程的工厂(线程的命名规则是“pool-数字-thread-数字”)
  • handler:线程池的饱和策略.

线程池工作的机制

  • 当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize;
  • 如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行;
  • 如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize.
  • 当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务

什么是阻塞队列

队列

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。

队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表。

阻塞队列
  • 支持阻塞的插入方法:意思是当队列满时,队列会阻塞插入元素的线程,直到队列不满。
  • 支持阻塞的移除方法:意思是在队列为空时,获取元素的线程会等待队列变为非空
方法 抛出异常 返回特殊值 一直阻塞
插入方法 add(e) offer(e) put(e)
移除方法 remove() poll() take()
常用的阻塞队列
  • ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
  • LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
  • PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
  • DelayQueue:一个使用优先级队列实现的无界阻塞队列。
  • SynchronousQueue:一个不存储元素的阻塞队列。
  • LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
  • LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

RejectedExecutionHandler(饱和策略)

  1. AbortPolicy:直接抛出异常,默认策略;
  2. CallerRunsPolicy:用调用者所在的线程来执行任务;
  3. DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
  4. DiscardPolicy:直接丢弃任务

合理配置线程池

  1. CPU密集型任务应配置尽可能小的线程,如配置Ncpu+1个线程的线程池。
  2. IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如2*Ncpu。
  3. 混合型的任务,如果可以拆分,将其拆分成一个CPU密集型任务和一个IO密集型任务,只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐量将高于串行执行的吞吐量

通过Runtime.getRuntime().availableProcessors()方法获得当前设备的CPU个数。

AsyncTask

AsyncTask是一个抽象类,内部其实他定义了两个线程池(ThreadPoolExecutor、SerialExecutor)以及一个Handler(InternalHandler)。而我们在使用AsyncTask的时候,一般会往这个类里面传递三个参数,Params,Progress,Result,而在这个类内部拥有四个方法。

class MyAsyncTask extends AsyncTask<String,Integer,String>{
 
        @Override
        protected void onPreExecute() {
            //数据初始化操作
            super.onPreExecute();
        }
 
        @Override
        protected String doInBackground(String... strings) {
            //耗时操作,并将结果返回
            return null;
        }
 
        @Override
        protected void onProgressUpdate(Integer... values) {
            //对进度条进行更新操作
            super.onProgressUpdate(values);
        }
 
        @Override
        protected void onPostExecute(String s) {
            //UI更新操作
            super.onPostExecute(s);
        }
    }
复制代码
  1. onPreExecute():我们在进行初始化数据的时候调用这个方法,这个方法是在主线程执行。
  2. doInBackground():我们在进行耗时操作的时候调用这个方法,所有的耗时操作都在这个里面进行操作。并且将耗时操作的结果返回回去。
  3. onProgressUpdata():对控件的进度进行操作。
  4. onPostExecute():这个方法里面会拿到doInBackground()方法中返回的参数结果,我们在这个方法里面可以对UI进行操作,从而达到更新UI的结果。

AsyncTask 源码分析:

构造函数
public AsyncTask(@Nullable Looper callbackLooper) {
    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
        ? getMainHandler()
        : new Handler(callbackLooper);
    //要执行的任务,是对Callable接口的封装。可以获取到返回值
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            mTaskInvoked.set(true);
            Result result = null;
            try {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                result = doInBackground(mParams);
                Binder.flushPendingCommands();
            } catch (Throwable tr) {
                mCancelled.set(true);
                throw tr;
            } finally {
                postResult(result);
            }
            return result;
        }
    };
    //AsyncTask要执行的任务分返回结果
    mFuture = new FutureTask<Result>(mWorker) {
        @Override
        protected void done() {
            try {
                postResultIfNotInvoked(get());
            } catch (InterruptedException e) {
                android.util.Log.w(LOG_TAG, e);
            } catch (ExecutionException e) {
                throw new RuntimeException("An error occurred while executing doInBackground()",
                        e.getCause());
            } catch (CancellationException e) {
                postResultIfNotInvoked(null);
            }
        }
    };
}
复制代码

AsyncTask的执行流程

execute->executeOnExecutor

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            case RUNNING:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task is already running.");
            case FINISHED:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task has already been executed "
                        + "(a task can be executed only once)");
        }
    }

    mStatus = Status.RUNNING;

    onPreExecute();

    mWorker.mParams = params;
    exec.execute(mFuture);

    return this;
}
复制代码

先回在主线程调用onPreExecute()方法。 将mFuture传递给了AsyncTask的执行器进行执行。AsyncTask的执行器缺省是sDefaultExecutor。

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

@UnsupportedAppUsage
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

private static class SerialExecutor implements Executor {
    //双端队列
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;

    public synchronized void execute(final Runnable r) {
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}
复制代码

每次调用execute,就创建一个Runnable匿名内部类对象,这个对象存入mTasks,在匿名内部类的run函数里面调用传入参数r.run()。然后通过一个scheduleNext函数把mTasks里面的所有对象通过THREAD_POOL_EXECUTOR.execute(mActive)执行一遍。

SerialExecutor类会把所有的任务丢入一个容器,之后把容器里面的所有对象一个一个的排队(串行化)执行THREAD_POOL_EXECUTOR.execute(mActive);

处理任务的线程池

static {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
            new SynchronousQueue<Runnable>(), sThreadFactory);
}
复制代码

SynchronousQueue没有容量,是无缓冲等待队列,是一个不存储元素的阻塞队列,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素。
以前用的是LinkedBlockingQueue(最大容量是128).

结果和进度的通知

private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}
复制代码

进入消息处理:

private static class InternalHandler extends Handler {
    public InternalHandler(Looper looper) {
        super(looper);
    }

    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
        AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
        switch (msg.what) {
            case MESSAGE_POST_RESULT://结果
                // There is only one result
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS://进度
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}
复制代码

小结

  • 在创建了AsyncTask的时候,会默认创建两个线程池SerialExecutor和ThreadPoolExecutor,SerialExecutor负责将任务串行化,ThreadPoolExecutor是真正执行任务的地方,且无论有多少个AsyncTask实例,两个线程池都会只有一份。

  • 在execute中,会执行run方法,当执行完run方法后,会调用scheduleNext()不断的从双端队列中轮询,获取下一个任务并继续放到一个子线程中执行,直到异步任务执行完毕。

  • 在执行完onPreExecute()方法之后,执行了doInBackground()方法,然后就不断的发送请求获取数据;在这个AsyncTask中维护了一个InternalHandler的类,这个类是继承Handler的,获取的数据是通过handler进行处理和发送的。在其handleMessage方法中,将消息传递给onProgressUpdate()进行进度的更新,也就可以将结果发送到主线程中,进行界面的更新了。

  • 通过观察代码我们可以发现,每一个new出的AsyncTask只能执行一次execute()方法,多次运行将会报错,如需多次,需要新new一个AsyncTask(参见如下代码)

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            case RUNNING:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task is already running.");
            case FINISHED:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task has already been executed "
                        + "(a task can be executed only once)");
        }
    }

    mStatus = Status.RUNNING;

    onPreExecute();

    mWorker.mParams = params;
    exec.execute(mFuture);

    return this;
}
复制代码

猜你喜欢

转载自juejin.im/post/7031399571909672990