引言:
说起线程池,学习过Java基础的同学估计都不陌生,Android中的线程池其实就是java的线程池。那么为什么线程池名声这么大,当然有其独特之处。这就引来了线程池到底有何优势。
线程池的优势:
Android中像访问内存卡,联网等耗时较长的任务时,需要在异步线程中进行,所以需要新开线程进行。但创建或者销毁线程伴随着系统的开销,频繁的创建和销毁线程,会较大的影响系统的性能。使用线程池,可用已有的闲置线程来执行新任务。
我们知道线程能共享系统资源,如果同时执行的线程过多,就有可能导致系统资源不足而产生阻塞的情况,运用线程池能有效的控制线程最大并发数,避免以上的问题。
线程池能够对线程进行有效的管理,比如延时执行,定时定周期循环执行等。
线程池ThreadPoolExecutor
既然线程池有这些优势,那么我们就要考虑怎样使用它。还是那句话,一个有追求的青年,要追根溯源。
android中的线程池来源于java中的线程池,那么我们就分析一下:
java中线程池的概念是一个接口,Excutor;其中ThreadPoolExecutor是这个接口的具体实现,也就是说线程池的所有定义及大部分操作将会从这个ThreadPoolExecutor中进行逻辑处理。那么我们就去看一下ThreadPoolExecutor的具体实现。
ThreadPoolExecutor的四个构造函数:
// 第一种构造器
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
throw new RuntimeException("Stub!");
}
// 第二种构造器
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
throw new RuntimeException("Stub!");
}
// 第三种构造器
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
throw new RuntimeException("Stub!");
}
// 第四种构造器
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
throw new RuntimeException("Stub!");
}
看到这些构造函数的参数是不是一脸懵逼,不要急不要慌,其实搞明白第一种构造函数就可以了其他三个都是鸡肋。下面我们就详细的分析一下这些参数。
-
corePoolSize:核心线程数 线程池新建线程的时候,如果当前线程总数小于corePoolSize,则新建的是核心线程,如果超过corePoolSize,则新建的是非核心线程;核心线程会一直保留,即使处于空闲状态,要想隔一段时间销毁它,必须指定ThreadPoolExecutor的allowCoreThreadTimeOut这个属性为true。
-
maximumPoolSize:线程池中线程的最大数 最大数 = 核心线程数 + 非核心线程数;其中非核心线程就是在线程池中的核心线程已满,但线程总数小于最大数时创建的线程为费核心线程。
-
keepAliveTime: 非核心线程保留时长 当非核心线程空闲时间超过保留时长时,就会被销毁掉。
-
TimeUnit:keepAliveTime的单位,TimeUnit是一个枚举类型
public enum TimeUnit {
DAYS,
HOURS,
MICROSECONDS,
MILLISECONDS,
MINUTES,
NANOSECONDS,
SECONDS; }
5 BlockingQueue 线程池中的任务队列:维护着等待执行的Runnable对象;当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务。
其他的两个参数基本不用,需要详细了解的可以自行查看源码。
ThreadPoolExecutor的具体使用
认识完ThreadPoolExecutor,接下来就看一下怎样使用。其实很简单;
① new ThreadPoolExecutor对象,参数自行配置
② 使用对象调用execute方法,方法里传入runnable接口
③ 上②的runnable接口就是需要执行的任务
经过上面三步就完成了一个任务的添加执行。
常见的四种线程池对象
我们牛逼的Google攻城狮给我们封装了四种常用的线程池对象,就是对ThreadPoolExecutor进行不同参数进行详细配置的具体实现。当然我们也可以按照上述三步走进行自定义ThreadPoolExecutor线程池。但一般情况这四种线程池足够我们使用。
CachedThreadPool(): 直接从源码中寻找答案:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
从上述源码可以看出可缓存线程池的特点:
① 线程数无限制
② 没有核心线程,都是非核心线程
使用方法:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
2 . FixedThreadPool():定长线程池
源码:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L,TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
特点:①:可控制线程最大并发数(线程数固定)
②:超出的线程会在队列中等待
使用方法:
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(int nThreads);
3 . ScheduledThreadPool():定时和周期性线程池
4 . SingleThreadExecutor():单一线程池
① 有且仅有一个工作线程执行任务
② 所有任务按照指定顺序执行,即遵循队列的入队出队规则
感谢
关于线程池的这篇文章参考(_liuzh)和(SEU_Calvin)的博客,感谢他们无私的分享。
———————————END BUT FINAL——————————-
网友评论