Android线程池深入浅出(一)

系列文章目录

第一章 Android线程池深入浅出(一)
第二章 Android线程池深入浅出(二)



前言

在Android开发中常在非UI线程中处理耗时任务,此时需要使用线程来处理异步任务,如果每次都创建一个线程并在执行完毕后销毁,那么在需要多线程使用频繁的场景会消耗大量的资源和性能的损耗。且线程容易各自为政,很难控制。

为了解决线程的使用弊端,在Java1.5中提供了Executor框架用于把任务的提交和执行解耦。任务的提交交给Runnable或者Callable,而Executor框架用来处理任务,Executor框架的核心实现类就是ThreadPoolExecutor


一、线程池的意义

程序中并发的线程数量有很多,如果每次手动创建只是执行了时间很短的任务就结束了,那么频繁的创建线程和销毁线程会降低系统的效率,并降低CPU的使用效率。为了节省资源,提高效率,Java中提供了线程池来解决问题。

线程池其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源


二、线程池有何优势

  1. 降低系统资源消耗,通过多次重用已存在的线程,降低线程创建和销毁造成的消耗;
  2. 提高系统响应速度,当有任务到达时,通过复用已存在的线程,无需等待新线程的创建便能立即执行;
  3. 提高线程的可管控性,根据系统得承载能力,调整工作线程的数目,防止内存占用过多出现OOM(每个线程消耗约1MB),同时线程数太多,频繁的CPU切换会产生高额的调度时间成本和降低CPU调度片段时间。并且可以使用线程池对线程进行统一分配、调优和监控。
  4. 提供更强大的功能,延时定时线程池。

三、线程池创建

Java里面线程池的顶级接口是java.util.concurrent.Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口java.util.concurrent.ExecutorService。

要配置一个线程池是比较复杂的,如果对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在java.util.concurrent.Executors线程工厂类里面提供了一些静态工厂,生成一些常用的线程池。官方建议使用Executors工程类来创建线程池对象,Executors是Executor,ExecutorService, ScheduledExecutorService,ThreadFactory,Callable的工具和工厂类。

如果是创建ThreadPoolExecutor,Executors调用newFixedThreadPool、newSingleThreadExecutor和newCachedThreadPool等方法创建线程池均是ThreadPoolExecutor类型。无论调用Executors类的何种创建方法,最终都会调用ThreadPoolExecutor类的构造函数。
代码如下:

/**
 *
 * @param corePoolSize 保留在池中的线​​程数,即使*处于空闲状态,
 *                     除非设置了{@code allowCoreThreadTimeOut}
 * @param maximumPoolSize 池中允许的最大线程数
 * @param keepAliveTime 当线程数大于核心时,这是多余的空闲线程在终止之前等待新任务的最长时间。
 * @param unit {@code keepAliveTime}参数的时间单位
 * @param workQueue 在执行任务之前用于保留任务的队列。该队列将仅保存由{@code execute}方法提交的{@code Runnable} *任务。
 * @param threadFactory 执行程序创建新线程时要使用的工厂
 * @param handler 当执行被阻塞时要使用的处理程序,因为达到了线程界限和队列容量,注意它并不是一个我们常见的Handler
 * @throws IllegalArgumentException 如果满足下列条件之一,则报IllegalArgumentException异常:
 *         {@code corePoolSize < 0}<br>
 *         {@code keepAliveTime < 0}<br>
 *         {@code maximumPoolSize <= 0}<br>
 *         {@code maximumPoolSize < corePoolSize}
 * @throws NullPointerException  如果 workQueue、threadFactory、handler之一为空,则报NullPointerException异常
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    
    
}

四、构造函数详解

参数 说明
corePoolSize 核心线程数。当线程数小于该值时,线程池会优先创建新线程来执行新任务
maximumPoolSize 线程池所能维护的最大线程数
keepAliveTime 空闲线程的存活时间
unit 参数keepAliveTime空闲线程的存活时间的单位 这是一个枚举类
workQueue 任务队列,用于缓存未执行的任务
threadFactory 线程工厂。可通过工厂为新建的线程设置更有意义的名字
handler 拒绝策略。当线程池和任务队列均处于饱和状态时,使用拒绝策略处理新任务。默认是 AbortPolicy,即直接抛出异常

1. corePoolSize
默认情况下线程池是空的,只是任务提交时才会创建线程。如果当前运行的线程数大于corePoolSize,线程看下情况创建。如果调用线程池的prestartAllcoreThread方法,线程池会提前创建并启动所有的核心线程来等待任务。

2. maximumPoolSize
如果任务队列满了并且线程数小于maximumPoolSize时,则线程池仍然会创建新的线程来处理任务

3. keepAliveTime
当线程数大于核心时,这是多余的空闲线程在终止之前等待新任务的最长时间,超过这时间则回收。

如果任务很多,并且每个任务的执行时间很短,则可以调大keepAliveTime来提高线程的利用率。另外,如果设置allowCoreThreadTimeOut属性来true时,keepAliveTime也会应用到核心线程上。

4. TimeUnit
keepAliveTime参数的时间单位。可选的单位有天Days、小时HOURS、分钟MINUTES、秒SECONDS、毫秒MILLISECONDS等。

5. workQueue
在执行任务之前用于保留任务的队列。该队列将仅保存由 execute 方法提交的 Runnable任务。该任务队列是BlockingQueue类型的,即阻塞队列。

6. ThreadFactory
可以使用线程工厂给每个创建出来的线程设置名字。一般情况下无须设置该参数。

7. RejectedExecutionHandler
拒绝策略。当执行被阻塞时要使用的处理程序,因为达到了线程界限和队列容量,注意它并不是一个我们常见的Handler,默认是AbordPolicy。它的几种策略方式在下一篇博客中会详细说明。

ThreadPoolExecutor的构造参数需要满足一定的条件:

  1. 如果出现如下其中一种情况,则构建中抛IllegalArgumentException异常
    1. corePoolSize < 0
    2. keepAliveTime < 0
    3. maximumPoolSize <= 0
    4. maximumPoolSize < corePoolSize
  2. 如果出现如下其中一种情况,则构建过程中抛NullPointerException异常
    1. workQueue is null
    2. threadFactory is null
    3. handler is null

五、常见的ThreadPoolExecutor

通过直接或间接的配置ThreadPoolExecutor的参数可以创建不同类型的ThreadPoolExecutor,其中有4种线程池比较常用:FixedThreadPool、CachedThreadPool、SingleThreadExecutor、ScheduledTheadPool。

1. FixedThreadPool


2. CachedThreadPool


3. SingleThreadExecutor


4. ScheduledTheadPool


总结

对于线程池中的线程创建策略、线程资源回收策略、线程排队策略、线程拒绝策略等进阶知识点请阅读我的另一边博文 Android线程池深入浅出(二)

猜你喜欢

转载自blog.csdn.net/luo_boke/article/details/107580536