Android线程以及线程池

线程在Android中的地位,相信每一位开发者都理解。基于Android的特性,UI线程即主线程主要用于界面的更新,子线程用于进行耗时任务。通过本篇文章,将学习主线程以及子线程的概念,android中的线程形态,包括我们熟悉的AsyncTask、HandlerThread、IntentService,最后,认识线程池在Android中的应用,以及主要的线程池分类。

主线程和子线程
主线程是指进程所拥有的线程,java中,默认情况下一个进程中只有一个线程,即主线程。主线程在任何时候都必须保持高度的流畅性,以应对用户与UI界面的交互。为了主线程的保持较高的响应速度,子线程就派上用场了。通常将比较耗时的操作放在子线程中执行。除了主线程之外的所有线程都叫子线程。Android沿用了java的线程机制,将线程分为UI线程和子线程,UI线程即主线程。UI线程运行四大组件以及它们与用户的交互,子线程执行比较耗时的操作,类似文件读写,网络请求等。

Android中的线程形态
我们知道,我们在实际的开发中,创建一个线程有多种方式,下面将从AsyncTask、HandlerThread、IntentService三者来对线程形态进行介绍。三者的底层实现都采用的是线程,但具有不同的表现形态,在使用中也各有优缺点。

1、AsyncTask

AsyncTask是一个轻量级的异步类,它可以在线程池中执行后台任务,并把执行任务进度以及结果返回给主线程,并更新UI。从实现上来说,AsyncTask内部封装了Thread和handler,可以很方便的执行后台任务和在主线程中更新UI。但是,AsyncTask不适合进行比较耗时的后台任务,下面先介绍AsyncTask的基本用法:

  • 创建AsyncTask
class myAsyncTask extends AsyncTask<String,Integer,Integer>{
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            Log.d("myAsyncTask","do onPreExecute");
        }

        @Override
        protected Integer doInBackground(String... files) {
            Log.d("myAsyncTask","do doInBackground");
            Integer cont = 0;
            for (String file : files){
                cont ++;
                Log.d("myAsyncTask","do file:" + file);
                publishProgress(cont);
            }
            return cont;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            for (Integer integer : values){
                Log.d("myAsyncTask","do onProgressUpdate:" + integer.intValue());
            }
        }

        @Override
        protected void onCancelled() {
            super.onCancelled();
        }

        @Override
        protected void onPostExecute(Integer integer) {
            super.onPostExecute(integer);
            Log.d("myAsyncTask","do onProgressUpdate:" + integer);
        }
    }
  • 开启AsyncTask
String[] files = {"file1","file2","file3"};
new myAsyncTask().execute(files);

从上面的代码可以知道,首先我们创建一个内部类,继承AsyncTask,并重写其相关方法。然后创建AsyncTask的对象实例,通过execute方法开启线程。AsyncTask类有三个参数Params,Progess,Result,三个参数都为泛型。Params是我们传入线程的参数,比如网络url,文件路径等,可以包含多个;Progess为线程在执行过程中返回的进度;Result为线程执行完后返回的结果。

  • onPreExecute:在主线程中执行,在异步线程开始之前被调用,一般做一些准备的工作。
  • doInBackground:在线程池中执行,此方法用于执行异步任务。通过publishProgress方法更新任务进度,publishProgress方法会调用onProgressUpdate方法。任务执行完后,会返回结果给onPostExecute。
  • onProgressUpdate:主线程中执行,在后台任务进度改变时被调用。
  • onPostExecute:在主线程中执行,异步线程任务执行完成后该方法被调用,参数为后台任务的返回值,即doInBackground的返回值。
  • onCancelled:主线程中执行,当后台任务被取消时,该方法被调用,onPostExecute方法将不再被调用。

AsyncTask方便了我们应用,但在使用中有一些限制:

  • 必须在主线程中创建和加载;
  • execute方法必须在UI线程调用;
  • 不要在程序中直接调用onPreExecute、doInBackground、onProgressUpdate、onPostExecute方法;
  • 一个AsyncTask对象只能调用一次,即只能执行一次execute方法,否则会报异常;
  • AsyncTask采用一个线程来串行执行任务,可以通过其executeOnExecutor方法来并行的执行任务。

上面介绍了AsyncTask的用法以及其在使用中应该注意的情况,下面通过分析AsyncTask的实现原理,就会比较理解AsyncTask的相关限制。
我们从execute方法开始分析:

    @MainThread
566 public final AsyncTask<Params, Progress, Result> execute(Params... params) {
567        return executeOnExecutor(sDefaultExecutor, params);
568    }

execute方法会调用executeOnExecutor方法:

603    @MainThread
604    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
605            Params... params) {
606        if (mStatus != Status.PENDING) {
607            switch (mStatus) {
608                case RUNNING:
609                    throw new IllegalStateException("Cannot execute task:"
610                            + " the task is already running.");
611                case FINISHED:
612                    throw new IllegalStateException("Cannot execute task:"
613                            + " the task has already been executed "
614                            + "(a task can be executed only once)");
615            }
616        }
617
618        mStatus = Status.RUNNING;
619
620        onPreExecute();
621
622        mWorker.mParams = params;
623        exec.execute(mFuture);
624
625        return this;
626    }

上面execute方法中的sDefaultExecutor是一个串行的线程池,一个进程中所有的AsyncTask都在该线程池中排队执行。在executeOnExecutor方法中,首先会对当前状态进行判断,处于runing、finshed状态都会抛出异常,只有在pending状态才向下执行。onPreExecute方法会被最先调用,然后开始后台任务。

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

 private static class SerialExecutor implements Executor {
236        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
237        Runnable mActive;
238
239        public synchronized void execute(final Runnable r) {
240            mTasks.offer(new Runnable() {
241                public void run() {
242                    try {
243                        r.run();
244                    } finally {
245                        scheduleNext();
246                    }
247                }
248            });
249            if (mActive == null) {
250                scheduleNext();
251            }
252        }
253
254        protected synchronized void scheduleNext() {
255            if ((mActive = mTasks.poll()) != null) {
256                THREAD_POOL_EXECUTOR.execute(mActive);
257            }
258        }
259    }

系统会将Params参数封装成FutureTask对象,FutureTask是一个并发对象,在这里充当Runnable对象。FutureTask会被交给SerialExecutor 的execute方法处理,将FutureTask插入到任务队列mTasks中。执行完任务后,会调用scheduleNext来开启下一个AsyncTask,直到所有任务被执行完为止。从这里可以看出,AsyncTask是串行执行的。
AsyncTask中有两个线程池(SerialExecutor 、THREAD_POOL_EXECUTOR)和一个handler(InternalHandler)。SerialExecutor 线程池用于任务的排队,THREAD_POOL_EXECUTOR则执行真正的后台任务。InternalHandler用于将执行环境切换到主线程。再AsyncTask的构造方法在中,有如下代码:

mWorker = new WorkerRunnable<Params, Result>() {
299            public Result call() throws Exception {
300                mTaskInvoked.set(true);
301                Result result = null;
302                try {
303                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
304                    //noinspection unchecked
305                    result = doInBackground(mParams);
306                    Binder.flushPendingCommands();
307                } catch (Throwable tr) {
308                    mCancelled.set(true);
309                    throw tr;
310                } finally {
311                    postResult(result);
312                }
313                return result;
314            }
315        };

FutureTask会调用mWorker 的run方法,因此mWork最终将在线程池中执行。再mWork的的call方法中,首先将mTaskInvoked设置true,表示任务已经被调用,然后执行doInBackground方法,最终将结果返回给postResult方法。

    private Result postResult(Result result) {
342        @SuppressWarnings("unchecked")
343        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
344                new AsyncTaskResult<Result>(this, result));
345        message.sendToTarget();
346        return result;
347    }

在postResult方法中,通过getHandler方法获取sHandler 对象并发送一条MESSAGE_POST_RESULT消息。

 private static Handler getHandler() {
281        synchronized (AsyncTask.class) {
282            if (sHandler == null) {
283                sHandler = new InternalHandler();
284            }
285            return sHandler;
286        }
287    }

该handler定义如下:

private static class InternalHandler extends Handler {
673        public InternalHandler() {
674            super(Looper.getMainLooper());
675        }
676
677        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
678        @Override
679        public void handleMessage(Message msg) {
680            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
681            switch (msg.what) {
682                case MESSAGE_POST_RESULT:
683                    // There is only one result
684                    result.mTask.finish(result.mData[0]);
685                    break;
686                case MESSAGE_POST_PROGRESS:
687                    result.mTask.onProgressUpdate(result.mData);
688                    break;
689            }
690        }
691    }

可以看到,该handler是一个静态对象,为了将执行环境切换到主线程,因此sHandler 必须在主线程创建。由于静态成员在类加载的时候会被初始化,因此这里变相的要求AsyncTask必须在主线程创建。收到MESSAGE_POST_RESULT消息后,会调用finish方法。

 private void finish(Result result) {
664        if (isCancelled()) {
665            onCancelled(result);
666        } else {
667            onPostExecute(result);
668        }
669        mStatus = Status.FINISHED;
670    }

在finish方法中,如果isCancelled方法被调用,即任务被取消,则调用onCancelled(result)方法,反之调用onPostExecute方法,参数为doInBackground方法的返回结果。

下面通过一个例子,验证AsyncTask的串行执行。
点击按钮时,同时开启五个任务,每个任务休眠5秒模拟耗时,最后打印任务执行完成的时间点。

mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new TestTask("task1").execute();
                new TestTask("task2").execute();
                new TestTask("task3").execute();
                new TestTask("task4").execute();
                new TestTask("task5").execute();
            }
        });

class  TestTask extends AsyncTask<String,Integer,String>{
        private String mName = "TestTask";

        public TestTask(String mName) {
            this.mName = mName;
        }

        @Override
        protected String doInBackground(String... strings) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return mName;
        }

        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
            SimpleDateFormat time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Log.d("TestTask",s + " finshed at time:" + time.format(new Date()));
        }
    }

结果:

D/TestTask: task1 finshed at time:2018-01-31 02:37:37
D/TestTask: task2 finshed at time:2018-01-31 02:37:42
D/TestTask: task3 finshed at time:2018-01-31 02:37:47
D/TestTask: task4 finshed at time:2018-01-31 02:37:52
D/TestTask: task5 finshed at time:2018-01-31 02:37:57

通过结果,我们可以再次验证AsyncTask在线程池中是串行执行的,当然,这是在Android3.0之后,之前将并行执行。为了能在3.0后并发执行,系统提供了executeOnExecutor方法。

new TestTask("task1").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
new TestTask("task2").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
new TestTask("task3").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
new TestTask("task4").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
new TestTask("task5").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");

结果:

D/TestTask: task2 finshed at time:2018-01-31 02:59:26
D/TestTask: task1 finshed at time:2018-01-31 02:59:26
D/TestTask: task3 finshed at time:2018-01-31 02:59:31
D/TestTask: task4 finshed at time:2018-01-31 02:59:31
D/TestTask: task5 finshed at time:2018-01-31 02:59:36

上面的代码运行在API 26上,发现通过executeOnExecutor方法开启并发任务时,最多允许两个AsyncTask任务并发进行。当我们调用execute方法时,系统会通过SerialExecutor 线程池对任务进行排队,最后调用executeOnExecutor串行执行;当我们直接调用executeOnExecutor方法是,跳过了中间的排队过程,直接通过THREAD_POOL_EXECUTOR线程池开启并发任务。
对于AsyncTask实现原理的分析就到这里了,总结一下:在AsyncTask中,一个两个线程池和一个Handler。SerialExecutor 线程池用于任务的排队,THREAD_POOL_EXECUTOR线程池用于执行具体的任务,当任务完成后,通过InternalHandler发送结果消息,并将执行环境切换到主线程。InternalHandler为静态对象,当加载类时,会自动初始化,这样就变相的要求AsyncTask在主线程创建。

2、HandlerThread

HandlerThread继承Thread,可以使用handler的线程。在其run方法中,通过Looper.prepare()创建消息循环队列,Looper.loop()开启消息循环。有了looper对象,这样就可以在HandlerThread中创建Handler对象。与常规Thread不同的是,一般的Thread通常执行一些耗时任务,而HandlerThread内部封装了消息循环,外界需要通过handler通知HandlerThread执行具体的任务。HandlerThread的run方法是一个死循环,因此当不再需要的时候,调用其 quit()或 quitSafely()方法跳出循环,终止线程的执行。

@Override
52    public void run() {
53        mTid = Process.myTid();
54        Looper.prepare();
55        synchronized (this) {
56            mLooper = Looper.myLooper();
57            notifyAll();
58        }
59        Process.setThreadPriority(mPriority);
60        onLooperPrepared();
61        Looper.loop();
62        mTid = -1;
63    }

3、IntentService

IntentService是一种特殊的service,并且是一个抽象类,因此必须为其创建子类才能实现它。IntentService用于执行后台任务,当任务执行完成后会自动停止。由于其属于Server,所以优先级会高于普通的线程,比较适合开启优先级较高的后台任务,不容易因系统内存不足而被杀死。实现上,IntentService封装了HandlerThread和Handler,二者在onCreate方法中被创建:

 @Override
104    public void onCreate() {
105        // TODO: It would be nice to have an option to hold a partial wakelock
106        // during processing, and to have a static startService(Context, Intent)
107        // method that would launch the service & hand off a wakelock.
108
109        super.onCreate();
110        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
111        thread.start();
112
113        mServiceLooper = thread.getLooper();
114        mServiceHandler = new ServiceHandler(mServiceLooper);
115    }

第一次启动IntentServer时,onCreate方法会被调用,首先创建HandlerThread 线程对象并开启线程。然后获取该线程的looper,并创建对应的mServiceHandler ,这样,由mServiceHandler 发送的消息都将被HandlerThread 线程处理执行。每次启动IntentServer,它的onStartCommand会被调用,用于处理每个后台任务的Intent。

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

onStartCommand会调用onStart方法:

 @Override
118    public void onStart(@Nullable Intent intent, int startId) {
119        Message msg = mServiceHandler.obtainMessage();
120        msg.arg1 = startId;
121        msg.obj = intent;
122        mServiceHandler.sendMessage(msg);
123    }

可以看出,在onStart方法中,通过mServiceHandler发送了Intent对象消息,因为mServiceHandler是在HandlerThread 中创建,所以消息会在HandlerThread 线程中被处理。mServiceHandler的handleMessage方法中,会将收到的intent对象交给 onHandleIntent方法处理。值得注意的是,这里的intent对象和启动server时startServer(intent)里的intent对象是一致的,这样在onHandleIntent方法中通过intent的不同参数开启不同的任务。

private final class ServiceHandler extends Handler {
62        public ServiceHandler(Looper looper) {
63            super(looper);
64        }
65
66        @Override
67        public void handleMessage(Message msg) {
68            onHandleIntent((Intent)msg.obj);
69            stopSelf(msg.arg1);
70        }
71    }

其中,onHandleIntent方法是一个抽象方法,需要在子类中实现。当onHandleIntent方法执行完后,系统会调用stopSelf(msg.arg1)来尝试停止服务。之所以用stopSelf(msg.arg1)而不是用stopSelf()方法是因为当前可能还有其他消息未处理,而stopSelf()方法会立即停止服务,stopSelf(msg.arg1)方法会等待所有消息处理完再停止服务。
由于每执行一次任务就必须启动一次IntentServer,而IntentServer内部是通过消息机制向HandlerThread 请求执行任务的,Handler中的looper是顺序处理消息的,因此,IntentServer也将顺序执行后台任务。
下面通过一个举例,来阐述IntentServer的用法。

public static class ServerTask extends IntentService{
        public ServerTask() {
            super("test");
        }

        @Override
        protected void onHandleIntent(@Nullable Intent intent) {
            String tag = intent.getStringExtra("action");
            Log.d("ServerTask","action: " + tag);
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onDestroy() {
            super.onDestroy();
            Log.d("ServerTask","onDestroy--------");
        }
    }

        Intent intent = new Intent(this,ServerTask.class);
        intent.putExtra("action","action_tag 1#");
        startService(intent);
        intent.putExtra("action","action_tag 2#");
        startService(intent);
        intent.putExtra("action","action_tag 3#");
        this.startService(intent);

在onHandleIntent方法中获取intent的参数值,可以对不同的intent进行过滤,执行不同操作。最后在onDestroy方法中进行日志打印,判断service自动结束的时间点。
结果打印:

D/ServerTask: action: action_tag 1#
D/ServerTask: action: action_tag 2#
D/ServerTask: action: action_tag 3#
D/ServerTask: onDestroy--------

从日志可以看出,任务执行的顺序是串行的,当所有的任务执行完成后,onDestroy方法被调用,服务停止。

线程池

提到线程池,首先说一下线程池的优点:

  • 重用线程池中的线程,避免创建或销毁线程带来的性能开销;
  • 能有效控制线程池的最大并发数,避免大量线程间相互抢占资源导致的阻塞现象;
  • 能够对线程进行简单的管理,并提供定时执行或指定间隔循环执行。

Android中的线程池来至与java中的Executor,Executor是一个接口,真正的线程池实现为ThreadPoolExecutor。ThreadPoolExecutor通过不同的参数来创建不同功能的线程池。
ThreadPoolExecutor的构造方法中,通过不同的参数配置线程池,下面通过ThreadPoolExecutor中的不同参数,来理解线程池:

 public ThreadPoolExecutor(int corePoolSize,
1182                              int maximumPoolSize,
1183                              long keepAliveTime,
1184                              TimeUnit unit,
1185                              BlockingQueue<Runnable> workQueue) {
1186        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
1187             Executors.defaultThreadFactory(), defaultHandler);
1188    }

corePoolSize
线程池的核心线程数,默认情况下,核心线程会一直存活,即使处于闲置状态。当ThreadPoolExecutor的allowCoreThreadTimeout属性设置为true时,则会有超时策略,时间由keepAliveTime决定,当超过该时间时,核心线程将被终止。

maximumPoolSize
线程池能容纳最多的线程数,当达到该上限后,后续的任务将会被阻塞。

keepAliveTime
非核心线程闲置状态超时时间,超过该时间,非核心线程将会被终止。当allowCoreThreadTimeout属性设置为true时,同样作用于核心线程。

unit
用于指定keepAliveTime时间单位,为一个枚举,包括毫秒、秒、分钟等

workQueue
线程池中的任务队列,通过线程池的execute方法提交的runnable对象会存储在该队列中。

ThreadFactory
线程工厂,为线程池提供创建新线程的接口。其只有一个方法,Thread newThread(Runnable r)。

除了上面的参数外,线程池还有一个特殊的参数Rejected-ExecutionHandler,当线程池无法执行任务时,有可能是线程任务已满或者是无法成功执行任务。这时候ThreadPoolExecutor会调用该handler的rejectedExeuction方法来通知调用者,默认情况下会抛出一个异常。

ThreadPoolExecutor执行任务时,遵循下面的规则:

  1. 如果线程池中的线程数量未达到核心线程数时,会开启一个新的核心线程来执行任务;
  2. 如果线程池中线程数量达到或超过核心线程数,则会被放入等待队列中排队执行;
  3. 上述二中,如果无法插入排队队列,那么一般情况是队列已满,此时如果线程数量未达到线程池规定的最大值,那么将开启非核心线程来执行任务;
  4. 上述三中,如果线程数量已经达到线程池规定的最大线程数,那么将拒绝执行该任务。

上面介绍了线程池,那么接下来介绍几种比较常用的线程池。

  • FixedThreadPool
    这是一种线程数量固定的线程池,当线程处于空闲状态时,并不会被回收,除非线程池被销毁。当所有线程都处于活跃状态时,新任务将等待,知道有空闲任务出来。由于该种线程池只有核心线程并且核心线程不会被回收,意味着它能够加速外界的请求。
  • CachedThreadPool
    这是一种线程数量不固定的线程池,并且只有非核心线程。最大线程数为integer.MAX_VALUE,由于integer.MAX_VALUE是一个很大的数,也就是说线程数可以任意大。当线程池中线程处于活跃状态时,会创建新的线程执行任务,否则将利用空闲线程,其中空闲线程的超时时间为一分钟。
  • ScheduleThreadPool
    核心线程是固定的,而非核心线程是没有限制的,并且非核心线程在闲置时会立即被回收。
  • SingleThreadExecutor
    该线程池内只有一个核心线程,并且确保所有任务都在同一个任务中执行,这样多任务间就不需要解决同步的问题。

猜你喜欢

转载自blog.csdn.net/Mr_azheng/article/details/79154905