线程池是多线程的一种处理方法。线程的创建和销毁需要消耗系统资源,使用线程池可以避免频繁的创建和销毁线程,具有以下优点:降低系统资源的消耗;提高响应速度;合理地管理线程,提高系统的稳定性。
Android采用的是Java的线程池。Java线程池的核心类是ThreadPoolExecutor,使用ThreadPoolExecutor创建线程池。下面对其进行详细说明。
corePoolSize & maximumPoolSize,核心线程数和最大线程数
线程池根据这两个参数自动调整线程的数量,当提交一个新的任务时,调整策略如下:
1、currentPoolSize < corePoolSize,创建一个新的线程。
2、corePoolSize <= currentPoolSize < maximumPoolSize,如果任务队列未满,加入任务队列等待;如果任务队列已满,创建一个新的线程。
创建线程
使用ThreadFactory创建线程,默认使用Executors.defaultThreadFactory(),创建的线程具有相同的优先级和non-daemon status,在同一个线程组。如果需要设定优先级,group,daemon status,请使用其他的ThreadFactory,线程需要处理RuntimePermission modifyThread,否则服务会被降级:设置的参数不能及时生效,线程池被关闭后可能停留在结束状态但是任务未完成。
On-demand 创建
默认情况下,只有任务到达时才会创建并启动线程。但通过方法prestartCoreThread()或者prestartAllCoreThreads()可以提前启动线程。
Keep-alive times
设置线程的闲置时长,当线程的闲置时长超过这个参数值并且currentPoolSize > corePoolSize,idle线程会被回收。
设置allowCoreThreadTimeOut(true),currentPoolSize <= corePoolSize,idle线程会被回收。
Queuing 等待队列
1、currentPoolSize < corePoolSize,创建一个新的线程执行任务,不会将任务加入等待队列。
2、currentPoolSize >= corePoolSize,将任务加入等待队列。
3、等待队列已满,currentPoolSize < maximumPoolSize,创建一个新的线程执行任务。
有三种等待策略:
1、SynchronousQueue,不保存提交的任务,直接新建线程来执行新任务,不需要进行等待,不能设置有边界的maximumPoolSize,否则新的任务可能被拒绝。适用于任务之间有相互的依赖。
2、LinkedBlockingQueue,基于链表的阻塞队列。如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE, 此时maximumPoolSize不起作用,最多有corePoolSize个线程在工作,适用于任务之间无依赖。
3、ArrayBlockingQueue,基于数组的有界阻塞队列, 线程数量不会超过maximumPoolSize。
拒绝策略
线程池已经关闭或者线程数量已满的情况下,提交新的任务会被拒绝。execute方法将调用RejectedExecutionHandler的rejectedExecution方法处理任务。有四种策略:
1、ThreadPoolExecutor.AbortPolicy,默认策略,丢弃任务并抛出异常RejectedExecutionException
2、ThreadPoolExecutor.CallerRunsPolicy,让调用者自己执行任务。
3、ThreadPoolExecutor.DiscardPolicy,直接丢弃。
4、ThreadPoolExecutor.DiscardOldestPolicy,丢弃队列头部的任务,再尝试,可能会重复丢弃-尝试。
线程池回收
当线程池内没有线程,并且不再被引用时,会自动回收。
线程池的关闭
ThreadPoolExecutor提供了两个方法,用于线程池的关闭。
shutdown():不会立即终止线程池,而是要等所有缓存队列中的任务都执行完后才终止,但不会接受新的任务。
shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务。
线程池的使用实例
在本例中,使用了ArrayBlockingQueue,拒绝策略使用ThreadPoolExecutor.DiscardPolicy。核心线程数是3,最大线程数是5,等待列大小为5。首先创建3个核心线程执行任务,接下来的任务放入等待队列,当等待队列满的时候,继续创建新的线程执行任务,线程数达到最大值5并且缓存队列已满的情况下,丢弃新到达的任务。
private void threadPool() {
int corePoolSize = 3;
int maximumPoolSize = 5;
int keepLiveTime = 50;
BlockingQueue blockingQueue = new ArrayBlockingQueue<Runnable>(5);
ThreadFactory threadFactory = new ThreadFactory() {
@Override
public Thread newThread(@NonNull Runnable r) {
ThreadGroup threadGroup = new ThreadGroup("worker");
Thread thread = new Thread(threadGroup, r);
thread.setDaemon(false);
thread.setPriority(Thread.NORM_PRIORITY);
return thread;
}
};
RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();
mThreadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
keepLiveTime, TimeUnit.SECONDS, blockingQueue, threadFactory, handler);
for (int i = 0; i < 12; i++) {
Runnable runnable = new Task(i);
mThreadPoolExecutor.execute(runnable);
Log.e(TAG, "current thread count:" + mThreadPoolExecutor.getPoolSize()
+ " number:" + i);
}
}
private class Task implements Runnable {
int taskId;
Task(int id) {
taskId = id;
}
@Override
public void run() {
try {
Log.d(TAG, "begin run in task:" + taskId);
Thread.sleep(10000);
}catch (Exception e) {
}
}
}
日志如下:
线程池的创建方法
Java中,不建议直接使用ThreadPoolExecutor创建线程池,提供了以下工厂方法创建线程池:
Executors.newSingleThreadExecutor() 线程池中线程的数量为1, 等待队列的大小为Integer.MAX_VALUE。
Executors.newCachedThreadPool() 线程的数量为Integer.MAX_VALUE,不保存任务。
Executors.newFixedThreadPool(int) 线程池中线程的数量为固定值,等待队列的大小为Integer.MAX_VALUE。
Executors.newScheduledThreadPool(int) 定时线程池。
四个方法的实现如下:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
ScheduledThreadPoolExecutor是ThreadPoolExecutor的子类。可用于实现定时工作。实例如下:
private void scheduledThreadPool() {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Log.e(TAG, format.format(new Date()));
Runnable runnable = new Runnable() {
@Override
public void run() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Log.e(TAG, "begin:" + format.format(new Date()));
try {
Thread.sleep(10000);
} catch (Exception e) {
}
Log.e(TAG, "end :" + format.format(new Date()));
}
};
//重复执行,初始延迟5秒执行,每一次任务执行时间指定为16秒(period参数)。
//如果period小于任务执行时间,则上一次任务结束后执行下一次任务。
//如果period大于任务执行时间,任务结束后等待period-任务执行时间,再执行下一次任务。
executorService.scheduleAtFixedRate(runnable, 5, 16, TimeUnit.SECONDS);
//只执行一次,延迟5秒执行
executorService.schedule(runnable, 5, TimeUnit.SECONDS);
//重复执行,初始延迟5秒执行,上一次任务执行结束后再等待16秒执行下一次任务。
//也就是任务之间的间隔是16秒。
executorService.scheduleWithFixedDelay(runnable, 5, 16, TimeUnit.SECONDS);
}