Android线程池的使用
线程池的概述
线程池可以看作是存放和管理多个线程的池子。在多任务情况下,多个线程的频繁创建和销毁会占用大量的资源,并影响性能。而线程池通过复用线程,避免了这种频繁的创建和销毁操作,从而提高了资源使用效率。
线程池的优势包括:
1.对线程进行统一管理,避免资源浪费。
2.复用线程,减少线程的创建和销毁开销。
3.有效控制线程池的最大并发数,避免线程之间因相互抢占系统资源而导致的阻塞现象。
线程池的使用
一:创建
//创建线程
ThreadPoolExecutor mThreadPoolExecutor = new ThreadPoolExecutor(
int corePoolSize, //核心线程数
int maximumPoolSize, //线程池最大线程数
long keepAliveTime, //线程活动保持时间
TimeUnit unit, //线程活动保持时间单位
BlockingQueue<Runnable> workQueue,//工作队列
ThreadFactory threadFactory)//线程工厂
//RejectedExecutionHandler handler)//异常处理(不常使用)
主要的参数解析以下:
1.corePoolSize(核心线程数):
- 线程池中始终运行的线程数量,即使这些线程处于空闲状态。
- 当线程数小于核心线程数时,新任务会创建一个新线程来执行,而不会排队。
2.maximumPoolSize(最大线程数):
- 线程池中允许的最大线程数量。
- 当工作队列满了之后,线程池会尝试创建新线程,直到线程数达到最大线程数。
如果超过了最大线程数,新任务会根据拒绝策略(RejectedExecutionHandler)来处理。
3.keepAliveTime(线程空闲时间):
- 当线程数量超过核心线程数时,这是超过核心线程数的线程在终止前等待新任务的最长时间。
- 只有当线程池中的线程数量大于核心线程数时,这个参数才会起作用。
4.unit(时间单位):
- keepAliveTime参数的时间单位,例如TimeUnit.SECONDS、TimeUnit.MILLISECONDS等。
5.workQueue(工作队列):
- 用于保存等待执行的任务的阻塞队列。
- 常见的实现有LinkedBlockingQueue(基于链表结构的阻塞队列,吞吐量通常要高于ArrayBlockingQueue)、ArrayBlockingQueue(基于数组结构的有界阻塞队列,在需要有序处理任务时很有用)、SynchronousQueue(一个不存储元素的阻塞队列,每个插入操作必须等待另一个线程的对应移除操作,反之亦然)等。
6.threadFactory(线程工厂):
- 用于创建新线程的工厂。
- 可以通过自定义的ThreadFactory来设置新创建线程的优先级、是否为守护线程、线程名称等属性。
- 如果不指定,默认使用Executors.defaultThreadFactory()。
7.handler(拒绝策略)
- 当线程池和工作队列都满了之后,用于处理被拒绝的任务的处理器。
- 常见的拒绝策略有:
ThreadPoolExecutor.AbortPolicy:抛出RejectedExecutionException。
ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)运行被拒绝的任务。
ThreadPoolExecutor.DiscardPolicy:不执行被拒绝的任务,也不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃最旧的未处理任务,然后尝试重新提交当前任务。
二:提交任务
//提交任务
mThreadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
... // 线程需要执行的任务
}
});
三:停止
shutdown()与shutdownNow()的区别:
- shutdown() :停止线程池接受新的任务提交,但允许已经提交的任务继续执行直到完成。任务队列中的任务也将被处理。
- shutdownNow():停止线程池接受新的任务提交,并尝试取消正在执行的任务。同时,清空任务队列。这个方法不会等待当前正在执行的任务完成,而是尝试中断它们。
isShutdown()与isTerminated()的区别
-
isShutdown():
功能:判断线程池是否已经开始关闭。
返回值:当调用shutdown()或shutdownNow()方法后,isShutdown()方法返回true,表示线程池已经开始关闭过程。 -
isTerminated():
功能:判断线程池是否已经完全关闭。
返回值: 当调用shutdown()方法后,并且线程池中的所有任务(包括正在执行的任务和队列中等待的任务)都完成后,isTerminated()方法返回true。 当调用shutdownNow()方法后,如果线程池成功停止(即尝试中断所有正在执行的任务,并返回等待队列中的任务),isTerminated()方法也会返回true。
//线程池停止
mThreadPoolExecutor.shutdown();
mThreadPoolExecutor.shutdownNow();
//线程池是否停止
mThreadPoolExecutor.isShutdown();
mThreadPoolExecutor.isTerminated();
线程的类型
FixedThreadPool(固定大小线程池)
- 特点:创建固定数量的线程来执行任务,不会动态调整线程数量。当线程池中所有线程都在执行任务时,新任务会被放入队列中等待执行。
- 适用场景:适用于需要控制并发线程数量的场景,可以避免因线程过多导致资源浪费。
- 优点:可以控制并发线程数量,避免因为线程过多而资源消耗太大。由于线程数量固定,可以避免因为线程的频繁创建和销毁带来的性能开销。
- 缺点:当任务数量超过了线程池的最大容量,超出的任务会被放在等待队列中等待执行,可能会导致队列堆积。
//通过newFixedThreadPool创建一个线程数量为8的线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(8);
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
}
});
ScheduledThreadPool (定时任务线程池)
- 特点:可以按照指定的时间间隔执行任务,也可以延迟执行任务。
- 适用场景:适用于需要定时执行任务的场景,如定时检查更新、定时清理缓存等。
- 优点:可以定时执行任务,避免了频繁创建销毁线程的开销。
- 缺点:如果任务执行时间过长,可能会影响后续任务的执行。
// 1. 通过ScheduledExecutorService创建线程数为8的线程池
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(8);
// 2. 创建好Runnable类线程对象
Runnable scheduledThreadPoolTask =new Runnable(){
public void run(){
//需要执行的任务
}
};
// 3. 提交任务
//参数(Runnable,延迟时间,延迟时间单位)
//延迟1000ms后执行任务
scheduledThreadPool.schedule(scheduledThreadPoolTask, 1000, TimeUnit.MILLISECONDS);
//参数(Runnable,延迟时间,间隔时间,时间单位)
// 延迟1000ms后、每隔1000ms执行任务
scheduledThreadPool.scheduleAtFixedRate(scheduledThreadPoolTask,1000,1000,TimeUnit.MILLISECONDS);
CachedThreadPool(缓存线程池)
- 特点:根据需要动态创建线程,如果线程空闲时间超过,则会被回收。线程池中的线程数量没有上限(理论上可以达到Integer.MAX_VALUE)。
- 适用场景:适用于执行大量短期异步任务的场景,如网络请求、数据处理等。
- 优点:根据需要动态创建线程,提高了资源利用率。
- 缺点:如果任务数量过多,可能会导致创建过多的线程,消耗大量系统资源。
//通过newCachedThreadPool创建一个线程数量为3的线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
}
});
SingleThreadExecutor(单线程线程池)
- 特点:只有一个线程的线程池,所有任务按顺序依次执行。
- 适用场景:适用于需要顺序执行任务的场景,如数据库操作的顺序执行。
- 优点:保证任务按照顺序执行,避免了多线程并发带来的同步问题,简化了编程模型。
- 缺点:单线程执行,可能会影响整体执行的效率。
//通过newSingleThreadExecutor创建一个线程数量为3的线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
singleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
}
});