在安卓开发中,线程池是一种管理线程的机制,可以有效地管理线程的创建,执行和销毁,从而提高应用的性能和效率。Android 提供了几种不同类型的线程池,每种线程池都有自己的特点和适用场景。
- FixedThreadPool(固定大小线程池)
- 固定大小线程池适用于需要控制并发线程数量的场景,它会创建指定数量的线程来执行任务,当所有线程都在忙碌的时候,新任务会被放在等待队列中,等待执行。
- 特点:
- 固定大小线程池创建固定数量的线程,不会动态调整线程数量
- 当线程池中所有线程都在执行任务时,新任务会被放入队列中等待执行
- 适用于需要限制并发线程数量的场景,可以避免因线程过多导致资源浪费
- 优点:
- 固定大小线程池是可以控制并发线程数量, 避免因为线程过多而资源消耗太大
- 由于线程数量固定,可以避免因为线程的频繁创建和销毁带来的性能开销
- 缺点:
- 当任务数量超过了线程池的最大容量,那么超出的任务会被放在等待队列中等待执行,可能会导致队列堆积
- 注意***
- 这个固定大小线程池核心线程数和最大线程数一样,意味着它里面全是核心线程,空闲线程存活时间为0毫秒这样空闲线程不会被销毁,任务队列采用的是LinkedBlockingQueue,需要注意这个队列有资源耗尽的风险,因为LinkedBlockingQueue的容量为Integer的最大值,而Integer的最大值是2的31次方,意味着任务数量高达21亿之多容易导致OOM。
- 工作原理:创建一个固定大小的线程池,当有任务提交时,线程池会按照任务的顺序创建线程来执行任务
- 适用场景:适用于并发任务数量可控场景,如限制同时下载文件的数量
- 代码
- CachedThreadPool(缓存线程池)
- 缓存线程池适用于执行大量短期异步任务的场景,它会根据需要创建新线程,在可用线程存在时会重用它们
- 特点
- 缓存线程池根据需要动态创建线程,适用于执行大量短期异步任务的场景
- 如果当前线程池中有可用线程,则重用;如果没有可用线程,则创建线程执行任务
- 可以根据任务的数量自动调整线程池大小,提高资源利用率
- 优点:
- 根据需要动态创建线程,适用于执行大量短期异步任务的场景
- 可以根据任务数量自动调整线程池大小,提高资源利用率
- 缺点:
- 如果任务数量过多,可能会导致创建过多的线程,消耗大量系统资源。
- 需要注意:
- 它里面是使用ThreadPoolExecutor来创建线程池,核心线程数量为0,意味着可缓存的线程池里面全都是非核心线程,非核心线程空闲时是会被销毁的,最大线程数是Integer的最大值,容易过大导致oom,空闲线程存活时间为60秒,任务队列使用的SynchronousQueue同步队列。
- 工作原理:
- 如果当前线程池中有可用线程,则重用;如果没有就创建新的线程来执行任务
- 适用场景:
- 适用于执行大量短期异步任务如网络请求,数据处理等
- 代码
- SingleThreadPool(单线程线程池)
- 单线程线程池适用于需要顺序执行的场景,它会创建单个工作线程来顺序执行所有任务
- 特点:
- 单线程线程池保证任务按顺序执行,不会并发执行
- 当前任务执行完毕后,才会执行下一个任务,适用于需要顺序执行任务的场景
- 由于只有一个线程,可以避免多线程并发带来的同步问题,简化了编程模型
- 优点:
- 保证任务按照顺序执行,适用于需要顺序执行任务的场景
- 缺点:
- 单线程执行,可能会影响整体执行的效率
- 工作原理:
- 创建一个单线程来顺序执行所有任务
- 需要注意:
- 它内部也采用的是ThreadPoolExecutor来创建线程池,并且核心线程和最大线程数都是为1,空闲线程存活时间为0毫秒也是不会被销毁,任务队列使用的也是LinkedBlockingQueue。
- 适用场景:
- 适用于需要保证任务按顺序执行场景,如数据库操作的顺序执行
- 代码
- ScheduledThreadPool(定时任务线程池)
- 定时任务线程池适用于需要定时执行任务的场景,它可以按照指定的时间间隔执行任务
- 特点:
- 定时任线程池可以按照指定的时间间隔执行任务,适用于需要定时执行任务的场景
- 可以延迟执行任务或者按照固定时间间隔执行任务
- 更加高效地执行定时任务,避免了频繁创建销毁线程的开销
- 优点:
- 可以定时执行任务,适用于需要定时执行任务的场景
- 缺点:
- 如果任务执行时间过长,可能会影响后续任务的执行
- 工作原理:
- 创建一个固定大小的线程池,可以按照指定的时间间隔执行任务
- 适用场景:
- 适用于需要定时执行任务的场景,例如定时检查更新,定时清理缓存等
- 代码
推荐使用ThreadPoolExecutor方式管理线程池,这样的处理方式更加明确线程池的运行规则,规避资源耗尽的风险,
提交任务的方式:
1、execute:
该方法位于Executor接口中,作用是向线程池提交runnable任务,runnable是无返回值的,所以这个方法只适合提交无返回值的任务。
2、submit: 该方法位于ExecutorService接口中,一共有三个submit方法,返回值类型都是Future,都带有泛型,任务执行结果在Future对象里面,Future是一个接口里面方法分别是cancel(boolean)是否取消任务,true取消,false不取消。get()获取任务执行结果。get(timeout, TimeUnit)在指定时间内获取任务执行结果,若超时则抛出异常。isCancelled()任务是否取消。isDone()任务是否完成。 Future对象可以观察到任务是否执行完毕。 get()方法是会阻塞线程的,获取结果也可以使用带有计时参数的get(timeout, TimeUnit)方法第一个入参是等待的最长时间,第二个方法是时间单位 ,意思是定时三秒钟,如果三秒过后没有结果就会继续执行下一个,如果执行出结果了就带着结果执行下一个。但是一般用无参get方法更多hhh。 cancel()方法取消任务,入参为true是尝试终断正在执行中的任务。 取消未执行的任务会引发CancellationException异常。 已完成的任务无法取消。 取消正在执行的任务如果入参传入的是false会提示返回任务取消成功但是会引发取消异常,并且任务继续执行没有停止。传入的true会取消成功,引发程序取消异常,但是需要响应中断线程指令或者等待线程继续执行完毕。
任务拒绝策略:
1、DiscardPolicy:什么也不做直接丢弃任务
2、CallerRunsPolicy:使用调用者线程直接执行被拒绝的任务
3、AbortPolicy:默认的拒绝策略,抛出RejectedExecutionException异常
4、DiscardOldestPolicy:丢弃处于任务队列头部任务,添加被拒绝的任务
线程池关闭: 1、shutdown:调用shutdown方法线程池不再接收新任务,会继续执行完任务队列中的任务 2、shutdownNow:关闭线程池,不再接受新的任务,尝试停止正在执行的任务,返回任务队列中的任务,shutdownNow一般都使用在需要立即关闭线程池的时候
线程池状态及生命周期:
1、RUNNING:可以接收新任务,并且也能处理任务队列中的任务
2、SHUTDOWN:不接受新提交得任务,但可以处理完已经在任务队列中的任务,执行完成后状态变为TIDYING
3、STOP:不接受新任务,也不处理任务队列中的任务,还会中断正在处理任务的线程
4、TIDYING:所有任务已经终止,线程池数量为0
5、TERMINATED:线程池彻底关闭
线程池的执行流程:
批量执行任务:
如何执行定时延时任务:
如何执行周期,重复性任务:
什么是ForkJoin框架
ForkJoin就是iba一个大任务分割分成若干个小任务,在对每个小任务得到的结果进行汇总,得到大任务结果的框架。
CompletionService: 返回结果采用执行优先原则,先执行完先返回,可以指定返回结果类型