Android-知识-018-Android多线程-线程池

91 阅读5分钟

Android 线程池

线程池(ThreadPool)是一种多线程管理机制,用于复用线程、减少线程创建和销毁的开销,从而提高应用的性能和资源利用率。Android 提供了线程池的相关类和接口,开发者可以通过它们来高效地管理和使用线程。


线程池的优点

  1. 减少资源开销
    线程池通过复用已创建的线程,避免频繁创建和销毁线程的开销。
  2. 提高响应速度
    由于不需要频繁创建线程,任务可以更快地开始执行。
  3. 方便管理线程
    提供任务队列和线程的管理,能够更好地控制并发数量,避免资源耗尽。
  4. 线程复用
    提高系统效率,避免因线程过多导致的内存占用和调度开销。

Android 中的线程池核心类

Android 基于 Java 的线程池,主要使用的是 java.util.concurrent 包中的类和接口。

1. Executor 接口

  • 顶层接口,用于执行任务,提供统一的线程管理入口。
  • 常用实现类是 ThreadPoolExecutor

2. ExecutorService 接口

  • 扩展了 Executor,提供了管理线程池的方法,如 shutdown()submit() 等。
  • 常用实现类是 ThreadPoolExecutor

3. ThreadPoolExecutor

  • 核心线程池实现类,可以自定义线程池的行为。
  • 通过其构造函数,可以设置核心线程数、最大线程数、线程存活时间等参数。

4. ScheduledThreadPoolExecutor

  • 用于定时或周期性执行任务。
  • 实现了 ScheduledExecutorService 接口。

5. Executors 工具类

  • 提供了一些静态方法,用于快速创建常见的线程池。

线程池的工作机制

线程池的工作机制可以总结为以下几个步骤:

  1. 提交任务: 任务通过 execute()submit() 提交给线程池。
  2. 任务队列: 任务首先被放入一个任务队列中。
  3. 核心线程: 如果线程池中的核心线程未达到 corePoolSize,则创建核心线程执行任务。
  4. 任务排队: 当核心线程已满时,任务会进入阻塞队列等待。
  5. 非核心线程: 如果任务队列已满且线程数小于 maximumPoolSize,则创建非核心线程处理任务。
  6. 拒绝策略: 如果线程池已满且任务队列也满,则根据拒绝策略处理任务。

ThreadPoolExecutor 构造方法

ThreadPoolExecutor 是线程池的核心实现类,构造方法如下:

java
复制代码
public ThreadPoolExecutor(
        int corePoolSize,        // 核心线程数
        int maximumPoolSize,     // 最大线程数
        long keepAliveTime,      // 非核心线程存活时间
        TimeUnit unit,           // 时间单位
        BlockingQueue<Runnable> workQueue, // 阻塞队列
        ThreadFactory threadFactory,       // 线程工厂
        RejectedExecutionHandler handler   // 拒绝策略
)

参数说明

  1. corePoolSize
    核心线程数,核心线程会始终保持存活,除非 allowCoreThreadTimeOut 被设置为 true
  2. maximumPoolSize
    最大线程数,包括核心线程和非核心线程。
  3. keepAliveTime
    非核心线程的空闲存活时间,当超过该时间且没有任务时,线程会被销毁。
  4. workQueue
    阻塞队列,用于存储等待执行的任务。
  5. threadFactory
    线程工厂,用于创建新线程。
  6. handler
    拒绝策略,当线程池无法执行任务时的处理策略。

常见线程池类型(Executors 工具类)

1. FixedThreadPool(固定线程数线程池)

java
复制代码
ExecutorService executor = Executors.newFixedThreadPool(4);
  • 特点

    • 核心线程数和最大线程数相等。
    • 使用无界队列存储任务。
  • 适用场景

    • 适用于负载稳定的场景,线程数固定避免资源耗尽。

2. CachedThreadPool(可缓存线程池)

java
复制代码
ExecutorService executor = Executors.newCachedThreadPool();
  • 特点

    • 线程数不固定,最大为 Integer.MAX_VALUE
    • 没有核心线程,线程空闲时间超过 60 秒会被销毁。
  • 适用场景

    • 适用于大量短时间任务,线程复用效率高。

3. SingleThreadExecutor(单线程线程池)

java
复制代码
ExecutorService executor = Executors.newSingleThreadExecutor();
  • 特点

    • 只有一个核心线程,确保任务按顺序执行。
  • 适用场景

    • 适用于需要保证任务顺序执行的场景。

4. ScheduledThreadPool(定时任务线程池)

java
复制代码
ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
  • 特点

    • 支持定时或周期性任务。
  • 适用场景

    • 适用于需要周期性执行任务的场景。

拒绝策略

线程池无法执行任务时,会根据指定的策略处理任务。常见策略有:

  1. AbortPolicy(默认)
    抛出 RejectedExecutionException
  2. CallerRunsPolicy
    使用提交任务的线程执行任务。
  3. DiscardPolicy
    丢弃任务,不抛出异常。
  4. DiscardOldestPolicy
    丢弃最早的任务,然后尝试执行新任务。

开发者也可以自定义拒绝策略,实现 RejectedExecutionHandler 接口。


示例:自定义线程池

java
复制代码
ThreadPoolExecutor executor = new ThreadPoolExecutor(
        2,                      // 核心线程数
        4,                      // 最大线程数
        60,                     // 非核心线程空闲存活时间
        TimeUnit.SECONDS,       // 时间单位
        new LinkedBlockingQueue<>(10), // 阻塞队列
        Executors.defaultThreadFactory(), // 线程工厂
        new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);

// 提交任务
for (int i = 0; i < 15; i++) {
    executor.execute(() -> {
        System.out.println(Thread.currentThread().getName() + " 正在执行任务");
    });
}

// 关闭线程池
executor.shutdown();

线程池的注意事项

  1. 避免使用 Executors 创建线程池

    • Executors 创建的线程池容易导致资源耗尽。
    • 推荐使用 ThreadPoolExecutor,更灵活、更可控。
  2. 线程池大小配置

    • 核心线程数可以根据设备 CPU 核心数设置。
    • 非核心线程数和队列大小应结合任务特点进行调整。
  3. 避免阻塞任务

    • 线程池中的任务不宜执行耗时的阻塞操作,以免影响其他任务。
  4. 线程池的生命周期管理

    • 使用 shutdown()shutdownNow() 释放线程池资源。

总结

线程池是 Android 开发中高效管理多线程任务的重要工具。它通过复用线程、合理控制并发数,减少了系统资源的浪费。开发者需要根据具体场景选择合适的线程池类型,并合理配置线程池参数以避免性能问题和资源耗尽。