Glide里面的几个线程池

本文介绍了Glide中线程池的使用,包括newDiskCacheExecutor、newAnimationExecutor、newSourceExecutor和newUnlimitedSourceExecutor四个线程池的详细配置和作用。newDiskCacheExecutor用于本地缓存,newAnimationExecutor处理GIF解析,newSourceExecutor和newUnlimitedSourceExecutor则处理网络请求。文章还讨论了如何关闭Glide的线程池。

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

一.概述

在日常开中,如果经常使用到线程的话,如何快捷创建和关闭回收线程是一件较为麻烦的事情,因此 阿里巴巴的Java规范插件 一直都是建议使用线程池来管理线程

使用线程池有那些好处?

  • 复用存在的线程,减少线程的创建,减少线程的开销,进一步提高了效率
  • 通过阻塞队列来控制最大的线程并发数,让内存等消耗限制在一个合理的范围内

二.线程池的相关参数

我们要怎么使用线程池呢?我们先来看下线程池参数最全面的构造方法吧:

    /**
     * 创建一个线程池
     * 
     *
     * @param corePoolSize  核心线程数量,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中
     *                      当线程池里面的线程数量超过corePoolSize就会启动回收操作
     *
     * @param maximumPoolSize 线程池里面的最大线程数
     *       
     * @param keepAliveTime  表示线程空闲时多久才将其回收,只有当线程池里面的线程数量超过corePoolSize才会生效,
     *                       它会让线程池里面的线程数量尽量等于或接近corePoolSize
     *       
     * @param unit keepAliveTime的时间单位
     *
     * @param workQueue 阻塞队列,用来存储等待执行的任务,等待线程池里面有空闲的线程来执行任务     
     *      
     * @param threadFactory 用来生产线程的工厂
     *        
     * @param handler 当任务无法处理时的策略
     *       
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

在明白了相关参数配置后,我们就可以开始依据自己需求来定制线程池了,可能你还是有点无从下手的感觉,没事,我们可以先看下其他人是怎么定制的,比方说看下 Glide 是怎么定制自己内部的线程池的

三.Glide 中定制的几个线程池

我们可以在 GlideGlideExecutor 里面找到它定制的线程池,Glide 定制了 4 个 线程池

1.newDiskCacheExecutor

这是用于执行本地缓存任务的线程池

public static GlideExecutor newDiskCacheExecutor(
      int threadCount, String name, UncaughtThrowableStrategy uncaughtThrowableStrategy) {
    return new GlideExecutor(
        new ThreadPoolExecutor(
            threadCount /* corePoolSize,数值为1 */,
            threadCount /* maximumPoolSize,数值为1 */,
            0 /* keepAliveTime为0 */,
            TimeUnit.MILLISECONDS,
            /*无序的阻塞队列,先进先出 */
            new PriorityBlockingQueue<Runnable>(),
            /*一个创建优先级为最低的后台线程的线程工厂*/
            new DefaultThreadFactory(name, uncaughtThrowableStrategy, true)));
  }

可以看出,newDiskCacheExecutor 这个线程池里面的核心线程1个,线程池最多持有线程数是1,线程的保活时间为0,线程工厂生产后台线程,可以说整个线程池由始至终就只有一个线程来循环使用来执行 Glide 的本地缓存任务

2.newAnimationExecutor

这是用于执行解析gif图片的线程池(它和 Glide 里面的动画逻辑没啥关系) , Glide 里面的 RequestOptions 里面有个 dontAnimate ,就是用来关闭配置整个线程池,从而将关闭 Glide 的 gif 图片加载功能(动画加载逻辑也会一同关闭)

//得到cpu的核数,返回值小于等于4
 public static int calculateBestThreadCount() {
    if (bestThreadCount == 0) {
      bestThreadCount =
          Math.min(MAXIMUM_AUTOMATIC_THREAD_COUNT,/*这个值是 4  */, RuntimeCompat.availableProcessors());
    }
    return bestThreadCount;
  }

 public static GlideExecutor newAnimationExecutor() {
    int bestThreadCount = calculateBestThreadCount();
    int maximumPoolSize = bestThreadCount >= 4 ? 2 : 1;
    return newAnimationExecutor(maximumPoolSize, UncaughtThrowableStrategy.DEFAULT);
 }

public static GlideExecutor newAnimationExecutor(
      int threadCount, UncaughtThrowableStrategy uncaughtThrowableStrategy) {
     return new GlideExecutor(
        new ThreadPoolExecutor(
            0 ,/* corePoolSize为0 */
            threadCount,/* maximumPoolSize,数值为2或者1 */
            KEEP_ALIVE_TIME_MS,/* keepAliveTime为10s的毫秒数 */
            TimeUnit.MILLISECONDS,
            /*无序的阻塞队列,先进先出 */
            new PriorityBlockingQueue<Runnable>(),
            /*一个创建优先级为最低的后台线程的线程工厂*/
            new DefaultThreadFactory(ANIMATION_EXECUTOR_NAME,uncaughtThrowableStrategy,true)));
  }
  

可以看出,newAnimationExecutor 这个线程池里面的核线程数量是0,线程池最多持有线程数是依据cpu核数来,高配手机是2个,低配手机是1个,线程工厂生产后台线程,这个线程池没有核心线程,只要将阻塞队列里面的任务全部完成后,线程也随即给回收掉,绝不会再消耗更多的内存

3.newSourceExecutor 和 newUnlimitedSourceExecutor

这两个线程池都是用于执行网络请求任务的,至于使用哪一个,则交给外层来配置了

 public static GlideExecutor newSourceExecutor() {
    return newSourceExecutor(
        calculateBestThreadCount(),
        DEFAULT_SOURCE_EXECUTOR_NAME,
        UncaughtThrowableStrategy.DEFAULT);
 }
 
 public static GlideExecutor newSourceExecutor(
      int threadCount, String name, UncaughtThrowableStrategy uncaughtThrowableStrategy) {
    return new GlideExecutor(
        new ThreadPoolExecutor(
            threadCount /* corePoolSize至少为4 */,
            threadCount /* maximumPoolSize至少为4 */,
            0 /* keepAliveTime为0 */,
            TimeUnit.MILLISECONDS,
            new PriorityBlockingQueue<Runnable>(),
             /*一个创建优先级为最低的后台线程的线程工厂*/
            new DefaultThreadFactory(name, uncaughtThrowableStrategy, false)));
  }

 .............

 public static GlideExecutor newUnlimitedSourceExecutor() {
    return new GlideExecutor(new ThreadPoolExecutor(
        0/* corePoolSize为0 */,
        Integer.MAX_VALUE /* maximumPoolSize为Integer.MAX_VALUE */,
        KEEP_ALIVE_TIME_MS /* keepAliveTime为 10s */,
        TimeUnit.MILLISECONDS,
        new SynchronousQueue<Runnable>() /* 这个阻塞队列不会保存任务,会立即创建新的线程来执行任务 */,
         /*一个创建优先级为最低的后台线程的线程工厂*/
        new DefaultThreadFactory(
            SOURCE_UNLIMITED_EXECUTOR_NAME,
            UncaughtThrowableStrategy.DEFAULT,
            false)));
  }
 

应该都看出来这两个线程池的区别了

  • newSourceExecutor : 核心线程数量和线程池最多持有线程数都是依据 CPU 核数来决定的,至少是4个,线程的保活时间为0,线程工厂设置为后台线程,它一直固定着最多 4 个线程来执行网络请求任务

  • newUnlimitedSourceExecutor : 核心线程数量为 0,线程池最大线程数是 Integer.MAX_VALUE,线程的保活时间为 10s ,线程工厂设置为后台线程,对 Java 的 4 大线程池 API 熟悉的话,就会发现这个其实就是个 改版的 newCachedThreadPool ,它会在第一时间去创建新的 线程来执行任务,在 10s ( newCachedThreadPoo设置是 60s 后开始回收) 后开始回收线程,缺点也很明显,如果一次性加入大 量的任务就会创建大量的线程,有 OOM 的风险,使用这个线程池必须非常小心

因此没有必要的话,建议还是使用 newSourceExecutor 执行网络请求任务

四.如何关闭线程池?

我们或者会遇到需要关闭线程池的情况,虽然线程池已经给我们提供了 shutdownshutdownNow 两个方法,但事实上这个并不能确保可以关闭线程池

Glide 提供了 tearDown 用于强行停止线程池,我们可以追踪进去看下它是怎么确保线程池的关闭:

 public static void shutdownAndAwaitTermination(ExecutorService pool) {
    long shutdownSeconds = 5;
    pool.shutdownNow();
    try {
      if (!pool.awaitTermination(shutdownSeconds, TimeUnit.SECONDS)) {
        pool.shutdownNow();
        if (!pool.awaitTermination(shutdownSeconds, TimeUnit.SECONDS)) {
          throw new RuntimeException("Failed to shutdown");
        }
      }
    } catch (InterruptedException ie) {
      pool.shutdownNow();
      Thread.currentThread().interrupt();
      throw new RuntimeException(ie);
    }
  }

我们可以追踪倒上面的 shutdownAndAwaitTermination 方法,可以看到如果遇到 shutdownNow 失败的情况,就抛出异常来关闭线程池

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值