Android的线程和线程池

线程是Android中的一个很重要的概念,从用途上来说:线程分为主线程和子线程,主线程主要处理和界面相关的事情,而子线程则往往用来执行耗时操作。
Android中的线程:

Thread
AsyncTask:封装了线程池和Handler,主要是方便开发者在子线程中更新UI。
IntentService:是一个服务,系统对其进行了封装使其可以更方便的执行后台任务,IntentService内部采用HandlerThread来执行任务,当任务执行完毕后,IntentService会自动退出。
HandlerThread:是一个具有消息循环的线程,在它的内部可以使用Handler。

主线程和子线程

主线程是指进程所拥有的线程,在java默认情况下一个进程只有一个线程,这个线程就是主线程。其他线程为子线程(工作线程)。

AsyncTask

AsyncTask是一种轻量级的异步任务类,他可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。

AsyncTask是一个抽象的泛型类,它提供了Params、progress、Result这三个泛型参数。

Params:参数类型
progress:后台任务的执行进度
Result:表示后台任务的返回结果

public abstract class AsyncTask<Params, Progress, Result> {}

AsyncTask提供了四个核心方法:

public static class myTask extends AsyncTask<Void,Integer,String>{
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }
        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
        }
        @Override
        protected String doInBackground(Void... params) {
            return null;
        }
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
        }
    }

(1)onPreExecute

在主线程中执行,在异步任务执行之前,次方法会被调用,一般可用于做一些准备工作。

(2)doInBackground(Void… params)

在线程中执行,次方法用于执行异步任务,params表示异步任务的输入参数。在此方法中可以通过publishProgress方法来更新任务的进度,publishProgress方法会调用onProgressUpdate方法。另外次方法需要返回结果给onPostExecute()方法。
(3)onProgressUpdate(Integer… values)
在主线程中执行,当后台任务的执行进度发生改变时此方法会被调用。

(4)onPostExecute(Result result)

在主线程中执行,在异步任务结束执行之后,此方法会被调用,其中result参数是后台任务的返回值。

上面的方法,onPreExecute先执行,然后是doInBackground最后才是onPostExecute。此外AsyncTask还提供了onCancelled()方法,同样在主线程中执行,当异步任务被取消时,onCancel()方法会被调用。

使用时注意事项:

1、AsyncTask对象必须在主线程中创建。
2、execute方法必须在UI线程中调用。
3、不要在程序中直接调用onPreExecute()、onPostExecute()、doInBackground和onProcessUpdate方法。
4、一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报运行时异常。
5、Android3.0以后通过executeOnExecutor方法来并行执行任务。

AsyncTask工作原理
分析AsyncTask的工作原理,首先从它的execute方法开始分析,execute方法又会调用executeOnExecutor方法。

@MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    /**sDefaultExecutor实际上是一个串行的线程池*/
        return executeOnExecutor(sDefaultExecutor, params);
    }
   @MainThread
    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方法*/
        onPreExecute();
        mWorker.mParams = params;
        exec.execute(mFuture);
        return this;
    }

然后线程池开始执行:

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

以上代码我们可以看到,AsyncTask在执行的过程中,首先会把Params参数封装成为FutureTask对象,FutureTask是一个并发类,在这里它充当了Runnable的作用。
接着这个FutureTask会交给SerialExecutor的execute方法去处理,execute方法首先会把AsyncTask对象插入到任务队列mTask中,如果这个时候没有正在活动的AsyncTask任务,那么就会抵用SerialExecutor 的scheduleNext方法来执行下一个AsyncTask任务。

AsyncTask中有两个线程池(SerialExecutor和THREAD_POOP_EXECUTOR)和一个Handler(InternalHandler),其中线程池SerialExecutor用于任务的排队,而线程池THREAD_POOP_EXECUTOR用于真正的执行任务,InternalHandler用于将执行环境从线程池切换到主线程。

在AsyncTask的构造方法中,有以下代码,由于Future的run方法会调用mWorker的call方法,因此mWorker的call方法最终会在线程池中进行。

mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                return postResult(result);
            }
        };

在上面代码中,mWorker 的call方法首先将mTaskInvoked设置为true,表示当前任务已经被调用过了,然后执行AsyncTask的doInBackground方法,接着将其返回值传递给postResult方法:

private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        /**通过sHndler发送一个MESSAGE_POST_RESULT的消息*/
        message.sendToTarget();
        return result;
    }

sHandler定义如下:sHandler是一个静态的Handler对象,为了能够将执行环境切换到主线程,这就要求sHandler对象必须在主线程中创建。

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

        @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
                    /**收到消息后调用AsyncTask的finish方法*/
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

我们再来看AsyncTask的finish方法。

private void finish(Result result) {
        if (isCancelled()) {
        //如果被取消,调用onCancelled方法
            onCancelled(result);
        } else {
        //否则调用onPostExecute方法
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

android 主线程耗时分析 安卓主线程和子线程_android 主线程耗时分析


这里需要注意一点:execute方法是静态方法,doInBackground方法是在构造方法中调用的。 所以execute后面调用的onpreExecute()方法是优先于doInBackground方法执行的。

HandlerThread

HandlerThread继承了Thread,它是一种可以使用Handler的Thread,它的实现很简单。就是在run方法中通过Looper.prepare()来创建爱你消息队列,并通过Looper.loop()来开启消息循环。这样在实际的应用中就允许在HandlerThread中创建Handler了。

@Override
    public void run() {
        mTid = Process.myTid();
        /**为当前线程创建Looper*/
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        /**开启消息循环*/
        Looper.loop();
        mTid = -1;
    }

HandlerThread一个具体的使用场景是IntentService,由于HandlerThread的run方法是一个无限循环,因此当不需要HandlerThread时,可以通过它的quit和quitSafely方法来终止线程的执行。

IntentService

IntentService是一种特殊的Service,它继承了Service 并且它是一个抽象类,因此必须实现它的子类才能使用IntentService。

IntentService可用于执行后台耗时的任务,当任务执行完成后它会自动停止,同时由于IntentService是服务的原因,这导致它的优先级比单纯的线程要高很多。所以IntentService比较适合执行一些高优先级的后台任务,不容易被系统杀死。

实际上,IntentService封装了HandlerThread和Handler,我们来看一下它的onCreate方法:

public abstract class IntentService extends Service {
            private volatile Looper mServiceLooper;
            private volatile ServiceHandler mServiceHandler;      
            @Override
            public void onCreate() {
                super.onCreate();
                HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
                thread.start();
                mServiceLooper = thread.getLooper();
                mServiceHandler = new ServiceHandler(mServiceLooper);
            }
        }

当IntentService 被第一次启动时,onCreate方法会创建一个HandlerThread,然后会使用它的Looper来构造一个Handler对象ServiceHandler,这样通过ServiceHandler发送的消息最终都会在HandlerThread中执行。

我们来看一下IntentService 的工作过程:我们首先看一下它的onStartCommand方法。

@Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

  @Override
    public void onStart(Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

onStart方法中使用mServiceHandler发送了一条消息,在HandlerThread中处理。

private final class ServiceHandler extends Handler {
                public ServiceHandler(Looper looper) {
                    super(looper);
                }
                @Override
                public void handleMessage(Message msg) {
                    onHandleIntent((Intent)msg.obj);
                    stopSelf(msg.arg1);
                }
            }

然后我们看到ServiceHandler处理消息,在onHandleIntent方法完成之后,使用stopSelf停止自身。

需要注意的是:如果目前只存在一个后台任务,那么onHandlerIntent方法执行完成这个任务后,stopSelf(msg.arg1);就会直接停止服务;如果目前存在多个后台任务,那么当onHandlerIntent方法执行完最后一个任务时,stopSelf(msg.arg1)才会直接停止服务。
Handler中的Looper是顺序处理消息的,所以IntentService 也是顺序执行后台任务的。

Android中的线程池

使用线程池的优点:

1、重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销。
2、能够有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
3、能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。

Android中的线程池概念来自于java中的Executor,Executor是一个接口,真正的线程池的实现为ThreadPoolExecutor。

ThreadPoolExecutor
构造方法:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

corePoolSize

线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使他们处于闲置状态。
还可以设置ThreadPoolExecutor等待任务的超时策略:allowCoreThreadTimeOut属性设置为true
maximumPoolSize
线程池所能容纳的最大线程数,如果线程数达到这个数值后,后续的任务会被阻塞。
keepAliveTime
非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收。
unit
用于指定keepAliveTime参数的事件单位,TimeUnit.MILLISECONDS(毫秒)、TimeUnit .SECONDS(秒)、TimeUnit MINUTES(分钟)等。
workQueue
线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中。

AsyncTask中线程池的配置情况:配置后的线程规格如下

1、核心线程数等于CPU核心数+1
2、线程池的最大线程数为CPU核心数的2倍+1
3、核心线程无超时机制,非核心线程在闲置时的超时时间为1秒
4、任务队列的容量为128

线程池的分类

1、FixedThreadPool

通过Execute的newFixedThreadPool方法来创建。它是一种线程数量固定的线程池,当线程处于空闲状态时,它们并不会被回收。

2、CachedThreadPool

通过Execute的newCachedThreadPool方法来创建。它是一种线程数量不定的线程池,只有非核心线程,并且最大线程数为Integer.MAX_VALUE。

3、ScheduledThreadPool

通过Execute的newScheduledThreadPool方法来创建。核心线程数量固定,非核心线程数量没有限制,而且非核心线程闲置时会被立即回收。
4、SingleThreadExecutor
通过Execute的newSingleThreadExecutor方法来创建。内部只有一个核心线程,并且确保所有的任务都在同一个线程中按顺序进行。