Android的线程和线程池

此篇为《Andoroid开发艺术探索》第11章Android的线程和线程池的读书笔记

除了Thread本身以外,在Android中可以扮演线程角色的还有很多,比如AsyncTaskIntentService,同时HandlerThread也是一种特殊的线程。对于AsyncTask来说,它的底层用到了线程池,对于IntentServiceHandlerThread来说,它们的底层则直接使用了线程。

不同形式的线程虽然都是线程,但是它们仍然具有不同的特性和使用场景。AsyncTask封装了线程池和Handler,它主要是为了方便开发者在子线程中更新UIHandlerThread是一种具有消息循环的线程,在它的内部可以使用HandlerIntentService是一个服务,系统对其进行了封装使其可以更方便地执行后台任务,IntentService内部采用HandlerThread来执行任务,当任务执行完毕后IntentService会自动退出。从任务执行的角度来看,IntentService的作用很像一个后台线程,但是IntentService是一种服务,它不容易被系统杀死从而可以尽量保证任务的执行,而如果是一个后台线程,由于这个时候进程中没有活动的四大组件,那么这个进程的优先级就会非常低,会很容易被系统杀死,这就是IntentService的有点。

线程是系统调度的最小单元,线程是一种受限的系统资源,它的创建和销毁都会有相应的开销。当系统中存在大量的线程时,系统会通过时间片轮转的方式调度每个线程,因为线程不可能做到绝对的并行。通过线程池可以避免因为频繁创建和销毁线程所带来的系统开销,一个线程池中会缓存一定数量的线程。Android中的线程池来源于java,主要通过Executor来派生特定类型的线程池,不同种类的线程池又具有各自的特性。

11.1 主线程和子线程

主线程是指进程所拥有的线程,在Java中默认情况下一个进程中只有一个线程,这个线程就是主线程。除了主线程以外的线程都是子线程,子线程也叫工作线程。

Android沿用了Java的线程模式。其中主线程也叫UI线程,主线程的作用是运行四大组件以及处理它们和用户的交互,而子线程的作用则是执行耗时任务,比如网络请求、I/O操作等。从Android3.0开始系统要求忘了访问必须在子线程中进行,否则网络访问将会失败并抛出NetworkOnMainThreadException这个异常,这样做是为了避免主线程由于被耗时操作所阻塞从而出现ANR现象。

11.2 Android中的线程形态

除了传统的Thread以外,还包含AsyncTaskHandlerThread以及IntentService,这三者的底层实现也是线程,但是它们具有特殊的表现形式,同时在使用上也各有优缺点。

11.2.1 AsyncTask

AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程中更新UI。从实现上来说,AsyncTask封装了ThreadHandler,通过AsyncTask可以更加方便地执行后台任务以及在主线程中访问UI,但是AsyncTask并不适合进行特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池。

 

AsyncTask类声明和参数说明如下:

/**

* AsyncTask是一个抽象的泛型类

* Params:参数的类型

* Progress:后台任务的执行进度的类型

* Result:后台任务返回结果的类型

* 如果不需要传递具体的参数,这三个泛型参数可以用Void来代替

*/

public abstract class AsyncTask<Params, Progress, Result>

 

AsyncTask提供了4个核心方法,相关说明如下所示:

private class AsyncDemo extends AsyncTask<Integer, Integer, String> {

 

/**

* 主线程中执行,异步任务执行之前调用,可以用于做一些准备工作

*/

@Override

protected void onPreExecute() {

super.onPreExecute();

}

/**

* 线程池中执行,用于执行异步任务,params表示异步任务的输入参数

* 通过调用publishProgress()来更新任务的进度,publishProgress会调用onProgressUpdate

* 此外这个方法需要return计算结果给onPostExecute()方法

*/

@Override

protected String doInBackground(Integer... params) {

return null;

}

/**

* 主线程中执行,当后台任务的执行进度发生改变时改方法会被调用

*/

@Override

protected void onProgressUpdate(Integer... values) {

super.onProgressUpdate(values);

}

/**

* 主线程中执行,异步任务执行之后该方法被调用

* resultdoInBackground的返回值

*/

@Override

protected void onPostExecute(String result) {

super.onPostExecute(result);

}

/**

* 主线程中执行,当异步任务被取消时该方法被调用

*/

@Override

protected void onCancelled() {

super.onCancelled();

}

}

当要执行异步任务的时候,可通过AsyncTaskexecute()方法开启异步任务。

 

AsyncTask的条件限制:

1) AsyncTask的类必须在主线程中加载。

2) AsyncTask的对象必须在主线程中创建。

3) execute方法必须在UI线程调用

4) 一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报运行时异常

5) Android3.0开始,AsyncTask采用一个线程来串行执行任务。我们可以通过executeOnExecutor方法来并行执行任务。

11.2.2 AsyncTask的工作原理

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

InternalHandler是一个静态的Handler对象,为了能够将执行环境切换到主线程,这就要求InternalHandler这个对象必须在主线程中创建。由于静态成员会在加载类的时候进行初始化,因此这就变相要求AsyncTask的类必须在主线程中加载,否则同一个进程中的AsyncTask都将无法正常工作。

11.2.3 HandlerThread

HandlerThread继承了Thread,它是一种可以使用HandlerThread

HandlerThread在内部创建了消息队列,外界需要通过Handler的消息方式来通知HandlerThread执行一个具体的任务。HandlerThreadrun方法是一个无限循环,当不需要使用HandlerThread时,可以通过它的quitquitSafely方法来终止线程的执行。

11.2.4 IntentService

IntentService继承了Service并且是一个抽象类。IntentService可用于执行后台耗时的任务,任务执行后它会自动停止。IntentService的优先级比单纯的线程要高很多,所以IntentService比较适合执行一些高优先级的后台任务,因为它的高优先级不容易被后台杀死。

11.3 Android中的线程池

线程池的优点:

1、 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销。

2、 能有效控制线程池的最大并发数,避免大量的线程之间因互抢占资源而导致的阻塞现象。

3、 能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。

 

Android中的线程池的概念来源于Java中的ExecutorExecutor是一个接口,真正的线程池的实现为ThreadPoolExecutorThreadPoolExecutor提供了一系列参数来配置线程池,通过不同的参数可以创建不同的线程池。

11.3.1 ThreadPoolExecutor

ThreadPoolExecutor一个比较常用的构造方法:

public ThreadPoolExecutor(int corePoolSize,

                              int maximumPoolSize,

                              long keepAliveTime,

                              TimeUnit unit,

                              BlockingQueue<Runnable> workQueue,

                              ThreadFactory threadFactory)

corePoolSize

线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使它们处于闲置状态。如果将ThreadPoolExecutorallowCoreThreadTimeOut属性设置为true,那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间由keepAliveTime所指定,当等待时间超过KeepAliveTime所指定的时长后,核心线程就会被终止。

maximumPoolSize

线程池所能容纳的最大线程数,当活动线程数达到这个数值后,后续的新任务将会被阻塞。

keepAliveTime

非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收。当allowCoreThreadTimeOut设置为true时,keepAliveTime同样会作用于核心线程。

unit

用于指定keepAliveTime参数的时间单位,这是一个枚举,常用的有TimeUnit.MILLISECONDS(毫秒)TimeUnit.SECONDS()以及TimeUnit.MINUTES(分钟)等。

workQueue

线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中。

threadFactory

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

 

ThreadPoolExecutor执行任务时大致遵循如下规则:

1、如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务。

2、如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行。

3、如果在步骤2中无法将任务插入到任务队列中,这往往是由于任务队列已满,这个时候如果线程数量未达到线程池规定的最大值,那么会立刻启动一个非核心线程来执行任务。

4、如果步骤3中线程数量已经达到线程池规定的最大值,那么就拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandlerrejectedExecution方法来通知调用者。

 

11.3.2 线程池的分类

Android中最常见的四类具有不同功能特性的线程池,都是直接或间接地通过配置ThreadPoolExecutor来实现自己的功能特性。

1、FixedThredPool

通过ExecutorsnewFixedThreadPool方法来创建。它是一种线程数量固定的线程池,当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭了。当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来。由于FixedThreadPool只有核心线程并且这些核心线程不会被回收,这意味着它能够更加快速地响应外界的请求。FixedThreadPool中只有核心线程并且这些核心线程没有超时机制,另外任务队列也没有大小限制。

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

 

2、CachedThreadPool

通过ExecutorsnewCachedThreadPool方法来创建。它是一种线程数量不定的线程池,它只有非核心线程。当线程池中的线程都处于活动状态时,线程池会创建新的线程来处理新任务,否则就会利用空闲的线程来处理新任务。线程池中的空闲线程的超时时长为60秒,超过60秒闲置线程就会被回收。CachedThreadPool的任何任务都会被立即执行。这类线程比较适合执行大量的耗时较少的任务。当整个线程池都处于闲置状态时,线程池中的线程都会超时而被停止,这个时候CacheThreadPool之中实际上是没有任何线程的。

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

 

3、ScheduledThreadPool

通过ExecutorsnewScheduledThreadPool方法来创建。它的核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会被立即回收。这类线程主要用于执行定时任务和具有固定周期的重复任务。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

public static ScheduledExecutorService newScheduledThreadPool(
        int corePoolSize, ThreadFactory threadFactory) {
    return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}

 

4、SingleThreadExecutor

通过ExecutorsnewSingleThreadExecutor方法来创建。这类线程池内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行。它的意义在于统一所有的外界任务到一个线程中,这使得在这些任务之间不需要处理线程同步的问题。

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}



目前先这样,后续会贴上demo代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值