Android中的线程池
前言
提到线程池,我们先说一下使用线程池的好处。使用线程池的优点可以概括为:
1、重复使用线程池中的线程,避免因为线程的创建和销毁带来的性能的开销而影响系统性能。
2、可以有效的控制线程的最大并发数,避免大量的线程之间因为互相抢占资源而导致堵塞现象。
3、对线程进行简单的管理,并提供定时执行以及循环间隔执行等功能。
一、ThreadPoolExecutor
在Android中的线程池来源于Java中的Executor,Executor是一个接口,真正的线程池实现类为Executor。下面我们来介绍一下ThreadPoolExecutor的创建方法。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
构造方法中有5个参数,接下来我们分别介绍这5个参数的含义:
corePoolSize:
线程池中核心的线程数,在默认的情况下核心线程会一直存活,即使是线程池中的线程处于闲置的状态。但是当ThreadPoolExecutor的allowCoreThreadTimeOut的属性设置为true的时候,即允许线程池中的核心线程被终止,通过keepAliveTime这个参数决定,这个参数意思就是线程池的闲置时长。当核心线程处于闲置状态并且超过了keepAliveTime超时时长,那么核心线程也会被终止(前提是allowCoreThreadTimeOut = true)
maximumPoolSize:
线程池中的最大线程数,当活动线程超过了最大线程数,那么后续的新任务就会处于阻塞状态。
keepAliveTime:
默认情况下指的是线程池中的非核心线程闲置的超时时长,当超过了这个时间,非核心线程就会被终止。当核心线程的allowCoreThreadTimeOut 属性为true的时候,核心线程闲置超过了该时长,也会被终止。
unit:
超时时长的单位
workQueue:
线程池中的任务队列,ThreadPoolExecutor中的execute方法传入的Runnable对象会存到盖队列中。
threadFactory:
线程工厂,为线程池创建新线程。
ThreadPoolExecutor执行任务的时候大致遵循以下原则,当有新任务要执行的时候。
(1)如果线程池中的核心线程未达到设置的核心线程数,那么就会开启一个核心线程执行新任务。
(2)如果线程池中的核心线程已经达到了设置的核心线程数,那么新的任务就会插入到线程池中的任务队列中等待被执行。
(3)如果(2)中的任务队列已满,并且线程池中的线程数量未达到设置的最大线程数,那么就会创建一个非核心线程去执行任务。
(4)如果(3)中线程池中的线程数已经达到了最大线程数,那么ThreadPoolExecutor就会调用RejectedExecutionHandler的rejectedExecution方法,通知调用者。
二、Android中常用的线程池分类
在Android中常用的线程池有四种:FixThreadPool、CachedThreadPool、ScheduleThreadPool、SingleThreadExecutor
(1)FixThreadPool(一堆人上公厕)
我们先看下FixThreadPool的初始化方法:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
我们看到核心线程数和最大线程数是一样的。所以使用FixThreadPool的时候,核心线程是固定的,并且没有非核心线程,任务都在核心线程中执行,当任务数超过了核心线程数,那么新任务就会在任务队列中排队。
(2)CachedThreadPool(一堆人去一家很大很大的咖啡馆)
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
从源码中我们看到,没有核心线程,并且非核心线程为Integer.MAX_VALUE。这个值非常大,所以每次有新任务的时候,当线程池中没有闲置的线程,就会创建新的线程执行新任务。线程池中的任务队列为SynchronousQueue,该队列可以当做一个无法存储元素的队列。
(3)ScheduleThreadPool
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue(), threadFactory);
}
ScheduleThreadPool的源码中可以看到,核心线程数是固定的,非核心线程不限制大小,当非核心线程闲置的时候,会被立即回收。任务队列我们看到是DelayedWorkQueue,从而看出,ScheduleThreadPool主要作用就是执行具有循环周期的定时任务。
(4)SingleThreadExecutor(公厕上只有一个坑位)
从英文单词上可以看出,就是单例的意思,我们来看下具体的实现:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
从源码中可以看到,核心线程数和非核心线程数都是固定的,当线程池中的线程非闲置的情况下,新的任务要插入到任务队列中排队。SingleThreadExecutor的意义在于统一所有的外界任务到一个线程中,使得任务之间不需要解决线程同步的问题。
小结
以上我们就简单的介绍了Android中的线程池的意义,初始化方法,以及在Android常用的线程池。
使用例子
ivEtxt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG, "run: "+Thread.currentThread().getName() );
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
Log.e(TAG, "run: "+Thread.currentThread().getName() );
}
});
}
});
08-27 15:31:34.567 3055-3055/com.mujin.keji.myapplication E/MainActivity: run: main
08-27 15:31:34.568 3055-3079/com.mujin.keji.myapplication E/MainActivity: run: pool-2-thread-1
初始化FixedThreadPool后,调用execute方法,传入Runnable对象,在run()方法中执行的任务就是在线程池中的线程执行的。我们看打印结果就知道,线程池的名字是不一样的。