一起Talk Android吧(第三百七十回:多线程之线程池回顾)

本文深入探讨了Android中线程池的使用,包括ExecutorService的创建、任务调度与拒绝策略。通过分析ThreadPoolExecutor的构造方法,详细解释了各个参数的作用,如核心线程数、最大线程数、存活时间和任务队列等。并指出Executors类静态方法创建线程池的潜在问题,可能导致内存溢出。建议根据项目需求自定义线程池以提高灵活性和性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

各位看官们,大家好,上一回中咱们说的是Android中多线程之按序操作的例子,这一回中咱们介绍的例子是多线程之线程池回顾。闲话休提,言归正转。让我们一起Talk Android吧!

看官们,我们在在之前的博客中介绍过线程池,如果有忘记的看官可以点击这点参考.

知识回顾

首先我们对线程池做个回顾:JUC中提供Executors类来操作线程池,该类提供了静态方法来创建线程池,比如newFixedThreadPool()/newCachedThreadPool()方法,这些方法返回ExecutorService接口类型的对象,然后使用此对象的execute()/submit()方法把线程添加到线程池中,线程池就会自动管理线程。添加线程的两个方法具有相同的功能,不过submit()方法更加灵活一些,它是重载方法,可以添加Runnable和Callable类型的线程对象到线程池中,相比而言execute()方法只能添加Runnable类型的线程对象到线程池中。最后是关闭线程池操作,不过实际项目中只有发生异常时才会关闭线程池,因为线程需要一直运行,不会停下来。

介绍这么多内容,我们将其总结成线程池使用三步曲:

  1. 创建线程池
  2. 向线程池中添加线程
  3. 关闭线程池

虽然使用Executors类的静态方法创建线程池比较灵活,但是存在一定的的缺陷:引起内存溢出(OOM),本章回中将对这些线程池的知识做扩展,主要是介绍如何创建个性化的线程池。大家可以看到,在程序中使用ExecutorService接口类型的对象表示线程池,JUC中还提供了ExecutorService接口的实现类:ThreadPoolExecutor来表示线程池,创建此类的对象相当于创建线程池。

介绍新内容

创建类的对象需要类的构造方法,因此我们重点看一下ThreadPoolExecutor类的构造方法.它的构造方法是重载方法 ,一共有四个,不同的地方在于参数数量,我们使用参数最多的哪个构造方法来介绍,明白这个构造方法后就可以理解其它的构造方法,下面是该方法的原型的文档:

   /**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code threadFactory} or {@code handler} is null
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 

文档中已经对构造方法及其的七个参数做了说明,不过我还要做以下的补充,以加深大家的理解:

  • param1:指定线程池中核线程的数量,不能小于0;
  • param2:指定线程池中最多可以容纳的线程数量,它的值大于等于核心线程数量;
  • param3:指定线程池中临时线程的存活时间;
  • param4:指定线程池中临时线程的存活时间单位;
  • param5:指定线程池中的任务队列,线程从任务队列中获取任务;
  • param6:指定线程池中创建线程的工厂方法;
  • param7:指定线程池中拒绝任务的策略;

明白构造方法后,我们介绍线程创建线程逻辑,通过这个逻辑可以更加深入了解这七个参数的含义和用法。大家都知道线程池自动管理线程(创建和销毁),那么线程池什么时候创建线程呢?通常会按照核心线程数量创建核心线程,如果核心线程都在工作而此时有新的工作任务到来时,把任务添加到任务队列中,等待核心线程完成当前工作后会从任务队列中取出任务继续工作,如果核心线程还在忙于当前工作任务而且任务队列也满了(这个要注意),那么创建临时线程,临时线程会从任务队列中取出任务来执行任务。注意核心线程加上临时线程的数量必须小于param2中的数量时才可以,不然创建临时线程时会发生异常。而当所有线程都在忙,而且任务队列也满了时,线程池开始拒绝任务,就会执行param7中的任务策略。

明白这个逻辑后,我们可以结合项目的需要,通过构造方法的参数自定义线程池中线程的数量、任务队列长度等内容。这样创建的线程池比较灵活,因此我们称其为个性化的线程池。回过头来再看看Executors类的静态方法如何创建线程池:

   public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

从代码中可以看到这两个静态方法也是是通过ThreadPoolExecutor类的构造方法创建线程池,不过它们默认指定了参数值,从指定的参数中可以看到创建Integer.MAX_VALUE这么大数量的线程池极有可能引起OOM。此外,把核心线程数量和最大线程数量设定为相同的值也不够合理,如果用户给参数赋值Integer.MAX_VALUE,同样会引起OOM。这些就是我们在文章开头时说过的“静态方法有缺陷”。

看官们,关于Android中多线程之按序操作的例子咱们就介绍到这里,欲知后面还有什么例子,且听下回分解!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

talk_8

真诚赞赏,手有余香

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值