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


    各位看官们,大家好,上一回中咱们说的是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:
    * {@code corePoolSize < 0}
    * {@code keepAliveTime < 0}
    * {@code maximumPoolSize <= 0}
    * {@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)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

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

    • 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);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

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

  • 相关阅读:
    java计算机毕业设计学生健康信息管理源码+系统+mysql数据库+lw文档
    记录一次循环引用的问题
    混合灰狼和布谷鸟搜索优化算法(Matlab完整代码实现)
    Kubernetes---使用 Helm 安装部署 Dashboard 仪表盘
    Android入门第6天-RelativeLayout
    【支付】支付安全
    day02IP地址详解以及简单的DOS命令
    DPDK之PMD原理
    BP神经网络的详细原理,基于BP神经网络的图像跟踪,基于BP神经网络的细胞跟踪,基于BP神经网络的视频跟踪
    智能化物流管理:全国快递物流查询API的角色与优势
  • 原文地址:https://blog.csdn.net/talk_8/article/details/126084172