Android中的线程与线程池
2016-03-12 17:02
453 查看
内容来自《Android艺术开发探索》
线程
(1)AsyncTask
AsyncTask使用一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把后台的进度和最终结果传递给主线程并在主线程中更新UI。从实现上来说,AsyncTask封装了Thread和Handler,AsyncTask不适合进行特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池。
AsyncTask是一个抽象的泛型类,它提供了Params、Progress和Result这三个泛型参数,Params表示参数的类型,Progress表示后台执行任务的进度,而Result表示后台任务返回的类型,如果AsyncTask不需要穿具体的参数,那么这三个泛型可以用Void来代替。
AsyncTask提供了4个方法,它们的含义如下所示。
a.onPreExecute(),在主线程中执行,在异步执行之前,此方法会被调用,一般可以用来做一些准备工作
b.doInBackground(Params… params),在线程池中执行,此方法用于执行异步任务,params参数表示的异步任务的输入参数。在此方法可以通过publishProgress方法来更新任务的进度,publishProgress方法会调用onProgressUpdate方法。另外doInBackground方法需要返回结果给onPostExecute。
c.onProgressUpdate(Progress… value), 在主线程中执行,当后台任务的执行进度发生改变时此方法会被调用。
d.onPostExecute(Result result),在主线程中执行,在异步任务执行之后,此方法会被调用,其中result参数是doInBackground的返回值。
上面这几个方法,onPreExecute先执行,接着是doInBackground,最后是onPostExecute。除了上述的四个方法以外,AsyncTask还提供了onCancelled()方法,它同样是在主线程中执行,当异步任务被取消的时候onCancelled()方法会被调用。下面一共一个典型的示例。
AsyncTask在具体的使用过程中也是有一些条件限制的,主要有如下几点:
a.AsyncTask的类必须在主线程中加载。
b.AsyncTask的对象必须在主线程中创建
c.execute方法必须在UI线程调用
d.不要在程序中直接调用onPreExecute、onPostExecute、doInBackground和onProgressUpdate方法
e.一个AsyncTask对象执行执行一次,即只能调用一次execute方法,否则会报异常
f.在1.6之前AsyncTask是串行执行任务的,1.6的时候AsyncTask开始采用线程里处理并行任务,再是从3.0开始,为了避免AsyncTask所带来的并发错误,AsyncTask有采用了一个线程来串行执行任务。尽管如此,在3.0以后的版本中,我们仍然可以用过AsyncTask的executeOnExecutor方法来并行的执行任务。
(2)HandlerThread
HandlerThread继承了Thread,它是一个可以使用Handler的Thread,它的实现也很简单,就是在run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环,这样在实际的使用中就允许在HandlerThread中创建Handler了。
通过以上三步我们就成功创建HandlerThread。通过handler发送消息,就会在子线程中执行。
如果想让HandlerThread退出,则需要调用handlerThread.quit()或handlerThread.quitSafely();。
(4)IntentService
IntentService是一种特殊的Service,它继承了Service并且它是一个抽象类。IntentService可用于执行后台耗时的任务,当任务执行完它会自动停止,同时由于IntentService是服务的原因,这导致它的优先级比单纯的进程高跟多,所以IntentService比较适合执行一些高优先级的后台任务,因为它优先级高不容易被杀死。在实现上IntentService封装了HandlerThread和Handler。
线程池
使用线程池的好处:
a.重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销
b.能有效的控制线程池的最大并发数,避免大量的线程之间因为互相抢占系统资源而导致阻塞现象
c.能对线程池进行简单的管理,并提供定时执行以及指定间隔循环执行等功能
(1)ThreadPoolExecutor
ThreadPoolExecutor是线程池的真正实现,它的构造方法提供了一系列参数来配置线程池,这些参数会直接影响到线程池的功能特性,下面是ThreadPoolExecutor的一个比较常用的构造方法。
corePoolSize,线程池的核心数量,默认情况下,核心线程会在线程池中一直存活,即使它们处于闲置状态。如果ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么闲置的核心线程在等待新任务到来时会有到时策略,这个时间间隔由keepAliveTime所指定,当时间超过keepAliveTime所指定的时长后,核心线程就会被终止
maximumPoolSize,线程池所能容纳的最大线程数,当活动线程达到这个数值后,后续的任务将会被阻塞。
keepAliveTime,非核心线程闲置的超时时长,超过这个时长,非核心线程就会被回收,当ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true时,keepAliveTime同样用于核心线程
unit,用于指定keepAliveTime参数的时间单位,这是一个枚举,常用的有TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES分钟等。
workQueue,线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中。
threadFactory,线程工厂,为线程池提供创建新线程功ThreadFactory是一个接口,它只有一个方法Thread newThread(Runnable r)。
ThreadPoolExecutor执行任务是大致遵循如下规则:
a.如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务
b.如果线程池中的线程属性已经到达或超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行。
c.如果在步骤b中无法将任务插入到任务队列中,这个往往是由于任务队列已满,这个时候如果线程数量为达到线程的最大值,那么会立刻启动一个非核心线程来执行
d.如果步骤c中线程数量已经达到线程规定的最大值,那么就拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler handler的rejectedExecution方法来通知调用者
(2)线程池的分类
android中常见的四类具有不同功能特性的线程池,它们都直接或间接的通过配置ThreadPoolExecutor来实现自己的功能特性。
FixedThreadPool,通过Executors.newFixedThreadPool()方法来创建。它是一种线程数量固定的线程池,当线程处于空闲状态是,它们并不会回收,除非线程池被关闭了。当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来。由于FixedThreadPool只有核心线程并且这些核心线程不会被回收,这意味着它能够更加快速的相应外界的请求。方法的实现如下所示,可以发现FixedThreadPool中只有核心线程并且这些核心线程没有超时机制,另外任务队列也是没有大小限制的。
CachedThreadPool,通过Executors.newCachedThreadPool()方法创建。它是一种线程数量不定的线程池,它只有非核心线程,并且其最大值为Integer.MAX_VALUE。线程池中的空闲线程都有超时机制,这个超时时长为60秒,超过60秒闲置线程就会被回收和ThreadPoolExecutor不同的是,CachedThreadPool的任务队列其实相对与一个空集合,这个导致任何任务都会立即被执行,因为SynchronousQueue是无法插入任务的。从CachedThreadPool的特性来看,这类线程池比较比较适合执行大量的耗时较少的任务。当整个线程池都处于闲置状态时,线程池中的线程都会超时而被停止,这个时候CachedThreadPool之中实际上是没有任何线程的,它几乎是不占用任何系统资源的newCachedThreadPool如下所示
ScheduledThreadPool,通过Executors.newScheduledThreadPool方法来创建。它的核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置是会被立即回收。ScheduledThreadPool这类线程又要用于执行定时任务和具有固定周期的重复任务。newScheduledThreadPool方法的实现如下所示。
SingleThreadExecutor,通过Executors.newSingleThreadExecutor方法来创建。这类线程池内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行。SingleThreadExecutor的意义在与统一所有外界的任务到一个线程中,这是的这些任务之间不需要处理线程同步问题newSingleThreadExecutor方法的实现如下所示。
线程
(1)AsyncTask
AsyncTask使用一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把后台的进度和最终结果传递给主线程并在主线程中更新UI。从实现上来说,AsyncTask封装了Thread和Handler,AsyncTask不适合进行特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池。
AsyncTask是一个抽象的泛型类,它提供了Params、Progress和Result这三个泛型参数,Params表示参数的类型,Progress表示后台执行任务的进度,而Result表示后台任务返回的类型,如果AsyncTask不需要穿具体的参数,那么这三个泛型可以用Void来代替。
AsyncTask提供了4个方法,它们的含义如下所示。
a.onPreExecute(),在主线程中执行,在异步执行之前,此方法会被调用,一般可以用来做一些准备工作
b.doInBackground(Params… params),在线程池中执行,此方法用于执行异步任务,params参数表示的异步任务的输入参数。在此方法可以通过publishProgress方法来更新任务的进度,publishProgress方法会调用onProgressUpdate方法。另外doInBackground方法需要返回结果给onPostExecute。
c.onProgressUpdate(Progress… value), 在主线程中执行,当后台任务的执行进度发生改变时此方法会被调用。
d.onPostExecute(Result result),在主线程中执行,在异步任务执行之后,此方法会被调用,其中result参数是doInBackground的返回值。
上面这几个方法,onPreExecute先执行,接着是doInBackground,最后是onPostExecute。除了上述的四个方法以外,AsyncTask还提供了onCancelled()方法,它同样是在主线程中执行,当异步任务被取消的时候onCancelled()方法会被调用。下面一共一个典型的示例。
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) * 100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; } protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } }
AsyncTask在具体的使用过程中也是有一些条件限制的,主要有如下几点:
a.AsyncTask的类必须在主线程中加载。
b.AsyncTask的对象必须在主线程中创建
c.execute方法必须在UI线程调用
d.不要在程序中直接调用onPreExecute、onPostExecute、doInBackground和onProgressUpdate方法
e.一个AsyncTask对象执行执行一次,即只能调用一次execute方法,否则会报异常
f.在1.6之前AsyncTask是串行执行任务的,1.6的时候AsyncTask开始采用线程里处理并行任务,再是从3.0开始,为了避免AsyncTask所带来的并发错误,AsyncTask有采用了一个线程来串行执行任务。尽管如此,在3.0以后的版本中,我们仍然可以用过AsyncTask的executeOnExecutor方法来并行的执行任务。
(2)HandlerThread
HandlerThread继承了Thread,它是一个可以使用Handler的Thread,它的实现也很简单,就是在run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环,这样在实际的使用中就允许在HandlerThread中创建Handler了。
//创建一个HandlerThread,即创建了一个包含Looper的线程。 HandlerThread handlerThread = new HandlerThread("com"); //创建HandlerThread后一定要记得start() handlerThread.start(); //获取HandlerThread的Looper Looper looper = handlerThread.getLooper(); //创建Handler,通过Looper初始化 final Handler handler = new Handler(looper);
通过以上三步我们就成功创建HandlerThread。通过handler发送消息,就会在子线程中执行。
如果想让HandlerThread退出,则需要调用handlerThread.quit()或handlerThread.quitSafely();。
(4)IntentService
IntentService是一种特殊的Service,它继承了Service并且它是一个抽象类。IntentService可用于执行后台耗时的任务,当任务执行完它会自动停止,同时由于IntentService是服务的原因,这导致它的优先级比单纯的进程高跟多,所以IntentService比较适合执行一些高优先级的后台任务,因为它优先级高不容易被杀死。在实现上IntentService封装了HandlerThread和Handler。
线程池
使用线程池的好处:
a.重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销
b.能有效的控制线程池的最大并发数,避免大量的线程之间因为互相抢占系统资源而导致阻塞现象
c.能对线程池进行简单的管理,并提供定时执行以及指定间隔循环执行等功能
(1)ThreadPoolExecutor
ThreadPoolExecutor是线程池的真正实现,它的构造方法提供了一系列参数来配置线程池,这些参数会直接影响到线程池的功能特性,下面是ThreadPoolExecutor的一个比较常用的构造方法。
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory
corePoolSize,线程池的核心数量,默认情况下,核心线程会在线程池中一直存活,即使它们处于闲置状态。如果ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么闲置的核心线程在等待新任务到来时会有到时策略,这个时间间隔由keepAliveTime所指定,当时间超过keepAliveTime所指定的时长后,核心线程就会被终止
maximumPoolSize,线程池所能容纳的最大线程数,当活动线程达到这个数值后,后续的任务将会被阻塞。
keepAliveTime,非核心线程闲置的超时时长,超过这个时长,非核心线程就会被回收,当ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true时,keepAliveTime同样用于核心线程
unit,用于指定keepAliveTime参数的时间单位,这是一个枚举,常用的有TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES分钟等。
workQueue,线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中。
threadFactory,线程工厂,为线程池提供创建新线程功ThreadFactory是一个接口,它只有一个方法Thread newThread(Runnable r)。
ThreadPoolExecutor执行任务是大致遵循如下规则:
a.如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务
b.如果线程池中的线程属性已经到达或超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行。
c.如果在步骤b中无法将任务插入到任务队列中,这个往往是由于任务队列已满,这个时候如果线程数量为达到线程的最大值,那么会立刻启动一个非核心线程来执行
d.如果步骤c中线程数量已经达到线程规定的最大值,那么就拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler handler的rejectedExecution方法来通知调用者
(2)线程池的分类
android中常见的四类具有不同功能特性的线程池,它们都直接或间接的通过配置ThreadPoolExecutor来实现自己的功能特性。
FixedThreadPool,通过Executors.newFixedThreadPool()方法来创建。它是一种线程数量固定的线程池,当线程处于空闲状态是,它们并不会回收,除非线程池被关闭了。当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来。由于FixedThreadPool只有核心线程并且这些核心线程不会被回收,这意味着它能够更加快速的相应外界的请求。方法的实现如下所示,可以发现FixedThreadPool中只有核心线程并且这些核心线程没有超时机制,另外任务队列也是没有大小限制的。
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
CachedThreadPool,通过Executors.newCachedThreadPool()方法创建。它是一种线程数量不定的线程池,它只有非核心线程,并且其最大值为Integer.MAX_VALUE。线程池中的空闲线程都有超时机制,这个超时时长为60秒,超过60秒闲置线程就会被回收和ThreadPoolExecutor不同的是,CachedThreadPool的任务队列其实相对与一个空集合,这个导致任何任务都会立即被执行,因为SynchronousQueue是无法插入任务的。从CachedThreadPool的特性来看,这类线程池比较比较适合执行大量的耗时较少的任务。当整个线程池都处于闲置状态时,线程池中的线程都会超时而被停止,这个时候CachedThreadPool之中实际上是没有任何线程的,它几乎是不占用任何系统资源的newCachedThreadPool如下所示
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
ScheduledThreadPool,通过Executors.newScheduledThreadPool方法来创建。它的核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置是会被立即回收。ScheduledThreadPool这类线程又要用于执行定时任务和具有固定周期的重复任务。newScheduledThreadPool方法的实现如下所示。
public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue()); }
SingleThreadExecutor,通过Executors.newSingleThreadExecutor方法来创建。这类线程池内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行。SingleThreadExecutor的意义在与统一所有外界的任务到一个线程中,这是的这些任务之间不需要处理线程同步问题newSingleThreadExecutor方法的实现如下所示。
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
相关文章推荐
- android ImageLoader加载本地图片的工具类
- Android源代码之DeskClock (一)
- 利用android剪贴板实现数据传递
- android studio 中制作简易的桌面组件
- android strings.xml的特殊字符
- 在android中如何使用全局变量
- Android 五种存储方式
- Android Animation
- Android 混淆代码总结
- Android Sqlite使用中注意事项
- Android学习之自定义控件之图片带文字的View
- Android MVVM的沉思
- Android开发之Android SDK Mannager更新失败
- 【读书笔记】【Android 开发艺术探索】第 6 章 Android 的 Drawable
- android TIF HAL层代码分析
- Android双向滑动冲突解决方案
- Android手动打包
- 【Music】横屏歌词显示效果不好
- 【Android测试】【随笔】测试分析——语法测试
- TUNA下载Android源码