在Java中默认情况下一个进程只有一个线程,也就是主线程,其他线程都是子线程,也叫工作线程。Android中的主线程主要处理和界面相关的事情,而子线程则往往用于执行耗时操作。线程的创建和销毁的开销较大,所以如果一个进程要频繁地创建和销毁线程的话,都会采用线程池的方式。
Android中线程的形态
传统的Thread
这是Java本身就支持的类,自定义化程度高,但是所有的功能都需要自己维护。
AsyncTask
AsyncTask常用于可以在几秒钟完成的后台任务。
HandlerThread
HandlerThread继承了Thread,是一种可以使用Handler的Thread,它的实现就是在run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环,这样在实际的使用中就允许在HandlerThread中创建Handler了。外界可以通过Handler的消息方式通知HandlerThread执行一个具体的任务。
HandlerThread的一个应用场景就是用在IntentService中。
HandlerThread的run方法是一个无限循环,因此当明确不需要再使用HandlerThread的时候,可以通过它的quit或者quitSafely方法来终止线程的执行,这是一个良好的编程习惯。
IntentService
IntentService是一个特殊的Service,它继承自Service并且是个抽象类,要使用它就要创建它的子类。与AsyncTask不同的是,IntentService用于需要长时间执行的任务,因为他是Service,所以他的优先级比单纯的线程高很多。
IntentService的onCreate方法中会创建HandlerThread,并使用HandlerThread的Looper来构造一个Handler对象ServiceHandler,这样通过ServiceHandler对象发送的消息最终都会在HandlerThread中执行。IntentService会将Intent封装到Message中,通过ServiceHandler发送出去,在ServiceHandler的handleMessage方法中会调用IntentService的抽象方法onHandleIntent,所以IntentService的子类都要是实现这个方法。
Android中的线程池
线程池的好处
(1)重用线程池中的线程,避免因为线程的创建与销毁带来的性能损耗。
(2)能有效的控制线程的最大并发,避免大量线程之间因为抢夺系统资源而带来的性能损耗而导致阻塞现象。
(3)能对线程进行简单的管理,并提供定时执行,以及提供定时执行以及指定间隔循环等功能。
Android中的线程池概念来源Java的Executor,Executor是一个接口,真正的线程池实现ThreadPoolExecutor.ThreadPoolExecutor提供了一系列参数来配置线程池,通过不同的参数创建不同的线程,从线程池的功能特性来说,Android的线程池可以分为4类.这四类线程池可以通过Executors所提供的工厂方法来得到.
ThreadPoolExecutor
ThreadPoolExecutor是线程的真正实现,他的的构造里面提供了一系列参数来配置线程池,下面一一对构造中的参数进行分析:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize:
线程池的核心线程数,默认情况下核心线程在线程池中会一直存活,即使他处于闲置状态,如果将ThreadPoolExecutor的allowCoreThreadTimerout属性设置为True,那么闲置的线程会有超时策略,这个时间由keepAliveTime所决定,当时间超过keepAliveTime之后,核心线程数会被终止。
maximumPoolSize:
最大线程数,当活动线程数达到这个数值后,后续的任务将会被阻塞。
keepAliveTime:
非核心线程闲置时的超时时长,超过这个时长,闲置的非核心线程就会被回收。
unit:
用于指定keepAliveTime参数的时间单位,有TimeUnit.MILLISECONDS、TimeUnit.SECONDS、TimeUnit.MINUTES等。
BlockingQueue:
任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中。
ThreadFactory :
线程工厂,为线程池提供创建新线程的功能。它是一个接口,它只有一个方法Thread newThread(Runnable r).
RejectedExecutionHandler :
当线程池无法执行新任务时,可能是由于任务队列已满或者是无法成功执行任务,这个时候就会调用这个Handler的rejectedExecution方法来通知调用者,默认情况下,rejectedExecution会直接抛出一个rejectedExecutionException。
ThreadPoolExecutor执行任务规则
(1)如果线程池中的线程数未达到核心线程的数量,那么会直接启动一个核心线程来执行任务
(2)如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行
(3)如果在步骤2中无法将任务插入到的任务队列中,可能是任务队列已满,这个时候如果线程数量没有达到规定的最大值,那么会立刻启动非核心线程来执行这个任务
(4)如果步骤3中线程数量已经达到线程池规定的最大值,那么就拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution方法来通知调用者。
AsyncTask中的THREAD_POOL_EXECUTOR线程池的配置情况:
public abstract class AsyncTask<Params, Progress, Result> {
private static final String LOG_TAG = "AsyncTask";
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
....
}
从上面AsyncTask源码中可以看出:
corePoolSize=CPU核心数+1;
maximumPoolSize=2倍的CPU核心数+1;
核心线程无超时机制,非核心线程在闲置时间的超时时间为1s;
任务队列的容量为128。
线程池分类:
FixedThreadPool:线程数量固定的线程池,它只有核心线程;
CachedThreadPool:线程数量不固定的线程池,它只有非核心线程;
ScheduledThreadPool:核心线程数量固定,非核心线程数量没有限制的线程池,主要用于执行定时任务和具有固定周期的任务;
SingleThreadPool:只有一个核心线程的线程池,确保了所有的任务都在同一个线程中按顺序执行。
下面通过实例来演示系统预装的4中线程池的使用:
private void runThreadPool() {
Runnable command = new Runnable() {
@Override
public void run() {
SystemClock.sleep(2000);
}
};
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4);
fixedThreadPool.execute(command);
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
cachedThreadPool.execute(command);
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(4);
// 2000ms后执行command
scheduledThreadPool.schedule(command, 2000, TimeUnit.MILLISECONDS);
// 延迟10ms后,每隔1000ms执行一次command
scheduledThreadPool.scheduleAtFixedRate(command, 10, 1000, TimeUnit.MILLISECONDS);
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
singleThreadExecutor.execute(command);
}