赞
踩
原文地址:http://blog.csdn.net/zxw136511485/article/details/51559759
我们在ListView中需要下载资源时,赞不考虑缓存机制,那么每一个Item可能都需要开启一个线程去下载资源(如果没有线程池),如果Item很多,那么我们可能就会无限制的一直创建新的线程去执行下载任务,最终结果可能导致,应用卡顿、手机反应迟钝!最坏的结果是,用户直接卸载掉该App。所以,我们在实际开发中需要考虑多线程,多线程就离不开线程池。如果你对线程还不了解,可以看看这篇文章,Android(线程一) 线程 。
使用线程池的优点:
(1).重用线程,避免线程的创建和销毁带来的性能开销;
(2).能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象;
(3).能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。
Android中的线程池是来自Java。那么就需要看看Java中的线程池。Java中的线程池有四种:
(1).SingleThreadExecutor,创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行;
(2).CachedThreadPool,创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程;
(3).FixedThreadPool,创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待;
(4).ScheduledThreadPool,创建一个定长线程池,支持定时及周期性任务执行。
PS:创建线程池,使用Executors类。通过这个类能够获得多种线程池的实例。Executors的核心构造方法ThreadPoolExecutor(),接下来,我们看看该方法说明,
参数说明:
corePoolSize 线程池维护线程的最少数量(核心线程数量),默认情况下,核心线程会在线程池中一直存活,即使它们处于闲置状态。如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么闲置的核心线程在等待新任务到来时会有超市策略,这个时间间隔由keepAliveTime所指定,当等待时间超过过keepAliveTime所指定的时长后,核心线程就会被终止。
maximumPoolSize 线程池维护线程的最大数量,当活动线程达到这个数值后,后续的新人无语将会被阻塞。
keepAliveTime 线程池中超过corePoolSize数目的空闲线程最大存活时间;可以allowCoreThreadTimeOut(true)使得核心线程有效时间。unit keepAliveTime时间单位,有7种取值,在TimeUnit类中有7种静态属性:
(6).当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭 。
PS:在ThreadPoolExecutor类中有几个非常重要的方法:
1.execute()
实际上是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现,这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。
2.submit()
在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果。
3.shutdown()
线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕;
4.shutdownNow()
线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务;
Future是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
在Future接口中声明了5个方法,下面依次解释每个方法的作用:
cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。
isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
isDone方法表示任务是否已经完成,若任务完成,则返回true;
get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
也就是说Future提供了三种功能:
1)判断任务是否完成;
2)能够中断任务;
3)能够获取任务执行结果。
构造一个只支持一个线程的线程池,配置corePoolSize=maximumPoolSize=1,无界阻塞队列LinkedBlockingQueue;保证任务由一个线程串行执行 。示例代码:
每隔2秒打印一次。结果依次输出,相当于顺序执行各个任务,运行截图:
现行大多数GUI程序都是单线程的。Android中单线程可用于数据库操作,文件操作,应用批量安装,应用批量删除等不适合并发但可能IO阻塞性及影响UI线程响应的操作。
这类线程池内部只有一个核心线程,它确保所有的任务都在同一个线程中顺序执行。SingleThreadExecutor的意义在于统一所有的外界任务到一个线程中,这使得在这些任务之间不需要处理线程同步的问题。
2.CachedThreadPool。
创建了5个线程分别执行不同的任务,运行截图:
它是一种线程数量不定的线程池,它只有非核心线程,并且最大线程数为231-1。由于231-1是一个很大的数,实际上就相当于最大线程数可以任意大。当线程池中的线程都处于活动状态时,线程池会创建新的线程来处理新任务,否则就会利用空闲的线程来处理新任务。线程池中的空闲线程都有超时机制,这个超时时长为60秒,超过60秒闲置的线程就会被回收。和FixedThreadPool不同的是,CachedThreadPool的任务队列其实相当于一个空集合,这将导致任何任务都会被立即执行,因为在这种场景下SynchronousQueue是无法插入任务的。这类线程池比较适合执行大量的耗时较少的任务。当整个线程池都处于闲置状态时,线程池中的线程都会超时而被终止,这个时候CachedThreadPool之中实际上是没有任何线程的,它几乎是不占用任何系统资源的。
它是一种线程数量固定的线程池,当线程处于空闲状态时,他们并不会被回收,除非线程池被关闭了。当所有的线程都处于活动状态时,新任务都处于等待状态,直到有线程空闲出来。由于FixedThreadPool 只有核心线程并且这些核心线程不会被回收,这意味着它能够更加快速地响应外界的请求。
4.ScheduledThreadPool。
super(corePoolSize, Integer.MAX_VALUE,DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue())的源码如下,依然是调用ThreadPoolExecutor去创建线程池,
示例代码:
它的核心线程数据是固定的,而非核心线程数是没有限制的,并且当非核心线程限制时会被立即回收。ScheduledThreadPool这类线程池主要用于执行定时任务和具有固定周期的重复任务。
总结:
(1).最大线程数一般设为2N+1最好,N是CPU核数;
(2).看过AsyncTask源码的同学就会知道,其实它的内部也是一个线程池;
(3).当创建线程池后,初始时,线程池处于RUNNING状态;
(4).如果调用了shutdown()方法,则线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕;
(5).如果调用了shutdownNow()方法,则线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务;
(6).当线程池处于SHUTDOWN或STOP状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为TERMINATED状态。
线程池作用就是限制系统中执行线程的数量。根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。
我们都知道Android系统提供了一个封装了线程和异步消息处理的类,AsyncTask即异步任务,该类中就涉及到线程池,详情请看,Android 源码解析AsyncTask(一)和Android 源码解析AsyncTask(二)。
PS:并行和并发区别
1、并行是指两者同时执行一件事,比如赛跑,两个人都在不停的往前跑;
2、并发是指资源有限的情况下,两者交替轮流使用资源,比如一段路(单核CPU资源)同时只能过一个人,A走一段后,让给B,B用完继续给A ,交替使用,目的是提高效率
有关并发和并行更加详细的描述,请看这篇文章, 并发和并行浅谈。
参考资料: 《Android开发艺术探索》。Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。