美文网首页
Android 线程池的实现和分类

Android 线程池的实现和分类

作者: 怡红快绿 | 来源:发表于2019-06-12 16:49 被阅读0次

一、什么是线程池

顾名思义,线程池就是一个可以同时容纳多个线程执行的容器。在多线程编程中,我们不可避免地要用到线程池技术,那么我们为什么要使用线程池呢?这就要说说线程池的优点了:

  • 通过重用线程池中的线程,可以避免因重复创建和销毁线程带来的性能开销;
  • 有效控制线程的最大并发数,避免大量的线程因互相抢占资源而造成系统的阻塞;
  • 能够对线程进行简单的管理,提供定时执行、间隔循环执行等功能。

二、线程池的实现

Android中线程池真正的实现是ThreadPoolExecutor类,它的构造方法提供了一系列参数来配置线程池,我们通过它的构造方法来了解各个参数的含义。

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}
  • corePoolSize
    线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使它们处于空闲状态。但是当ThreadPoolExecutor的allowCoreThreadTimeOut属性值为true,那么空闲的核心线程也会有超时策略,这个时长由keepAliveTime决定,当等待超过keepAliveTime指定的时长,核心线程被终止。
  • maximumPoolSize
    线程池能容纳的最大线程数,当线程数达到这个数值,后续的任务会被阻塞,直到有其他线程被释放。
  • keepAliveTime
    非核心线程的闲置超时时长,超过这个时长非核心线程就会被回收。当ThreadPoolExecutor的allowCoreThreadTimeOut属性值为true,keepAliveTime同样会作用于核心线程。
  • unit
    设置keepAliveTime参数的单位。
  • workQueue
    线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中。
  • threadFactory
    线程工厂,专门为线程池生成新线程。

ThreadPoolExecutor执行任务时需要遵循如下规则:

  1. 如果线程池中的线程数量未达到核心线程数,那么会直接启动一个核心线程来执行新的任务;
  2. 如果线程池中的线程数量已经达到或超过核心线程数,那么任务会被插入到任务队列中排队等待;
  3. 如果线程池中的线程数量已经达到或超过核心线程数,并且任务队列已满,这个时候如果线程池中的线程数量未达到线程池规定的最大值,那么会立即启动一个非核心线程来执行任务;
  4. 如果步骤3中线程数量达到线程池规定的最大值,那么就拒绝执行此任务,会调用RejectedExecutionHandler的rejectedExecution方法来通知调用者任务执行请求被拒绝。
处理任务优先级排序:启动核心线程 -> 进入任务队列 -> 启动非核心线程 。

我们接下来从源码的角度分析一下任务执行的规则:

public void execute(Runnable command) {
   //传进来的线程为null,则抛出空指针异常
   if (command == null)
       throw new NullPointerException();
  
   //获取当前线程池的状态+线程个数变量
   int c = ctl.get();
   /**
    * 3个步骤
    */
   //1.判断当前线程池线程个数是否小于corePoolSize,小于则调用addWorker方法创建新线程运行,且传进来的Runnable当做第一个任务执行。
   //如果调用addWorker方法返回false,则直接返回
   if (workerCountOf(c) < corePoolSize) {
       if (addWorker(command, true))
           return;
       c = ctl.get();
   }

   //2.如果线程池处于RUNNING状态,则添加任务到阻塞队列
   if (isRunning(c) && workQueue.offer(command)) {

       //二次检查
       int recheck = ctl.get();
       //如果当前线程池状态不是RUNNING则从队列删除任务,并执行拒绝策略
       if (! isRunning(recheck) && remove(command))
           reject(command);

       //否者如果当前线程池线程空,则添加一个线程
       else if (workerCountOf(recheck) == 0)
           addWorker(null, false);
   }
   //3.新增线程,新增失败则执行拒绝策略
   else if (!addWorker(command, false))
       reject(command);
}

这样直接看代码可能还是不太清晰,所以我们直接看图会比较容易理解具体过程。

图片来源于网络

三、线程池的分类

第二节中对ThreadPoolExecutor的主要配置参数进行了详细的介绍,本节将介绍Android中常见的几种线程池,看看它们是如何通过配置ThreadPoolExecutor来实现自己的功能特性。

(1)FixedThreadPool

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

总结:核心线程数=最大线程数,即线程池中只有核心线程,这意味着所有线程不会被回收,除非线程池被关闭了。当所有线程都处于活动状态时,由于任务队列没有限制大小,因此新任务总是会进入任务队列排队等待,直到有线程空闲出来。由于FixedThreadPool只有核心线程并且这些线程不会被回收,因此它能够更加快速地响应外界的请求

(2)SingleThreadExecutor

public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>(),
                                threadFactory));
}

总结:核心线程数=最大线程数=1,即线程池中只能有一个核心线程。SingleThreadExecutor可以确保所有任务都在同一线程中顺序执行,这使得在这些任务之间不需要处理线程同步问题

(3)CachedThreadPool

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

总结:核心线程数=0,即它只有非核心线程,并且线程池可以容纳的非核心线程数量为Integer.MAX_VALUE。线程池中的空闲线程都有超时机制,如果在60秒内没有执行任务,线程将会被系统回收。假如某个时刻所有线程都处于活动状态,那么就会创建新线程来执行新任务,否则直接使用空闲线程处理任务。CachedThreadPool的任务队列实际上是一个无法存储任务的队列,这将导致任何任务都会立即被执行。CachedThreadPool适用于执行大量耗时较少的任务。当所有线程都处于空闲状态时,它们会因为超时而全部被回收,这个时候CachedThreadPool中没有任何线程,这几乎不占用任何系统资源。

(4)ScheduledThreadPool

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue());
}
//延迟1秒执行,每隔1秒向线程池提交一次任务
Executors. newScheduledThreadPool (5).scheduleAtFixedRate(r, 1, 1, TimeUnit.SECONDS);

总结:核心线程数固定,非核心线程数为Integer.MAX_VALUE。非核心线程处于空闲状态超过一定时长就会被回收。ScheduledThreadPool主要用于执行定时任务以及有固定周期的重复任务

除了系统提供的这几类线程池之外,我们也可以根据实际场景灵活地配置线程池。


参考

《Android开发艺术探索》
java多线程系列:ThreadPoolExecutor源码分析

相关文章

  • Android的线程和线程池

    Android的线程和线程池 标签(空格分隔): android 一些概念: 线程分类:主线程和子线程,主线程主要...

  • Android 线程池的实现和分类

    一、什么是线程池 顾名思义,线程池就是一个可以同时容纳多个线程执行的容器。在多线程编程中,我们不可避免地要用到线程...

  • Android ThreadPoolExecutor线程池

    引言 Android的线程池概念来自于Java的Executor,真正的线程池实现为ThreadPoolExecu...

  • Android的线程池

    Android中的线程池来源于Java中的Executor接口,真正的线程池实现为ThreadPoolExecut...

  • 线程池

    线程池简介: android提供了四种线程池,都是由直接或简介配置ThreadPoolExecutor来实现的。 ...

  • Android线程池的使用

    一、线程与线程池,为什么要使用线程池 1、Android中的线程 在Android中有主线程和子线程的区分。主线程...

  • 线程池

    1. Android中的线程池有哪些,它们的区别是什么? ThreadPoolExecutor是线程池的真正实现,...

  • Android 线程池相关知识

    Android中的线程池都是之间或间接通过配置ThreadPoolExecutor来实现不同特性的线程池.Andr...

  • Android 线程池的相关知识

    Android中的线程池都是直接或间接通过配置ThreadPoolExecutor来实现不同特性的线程池.Andr...

  • 笔记:Android线程和线程池

    Android线程和线程池 Android中的线程操作相关的类有 AsyncTask IntentService ...

网友评论

      本文标题:Android 线程池的实现和分类

      本文链接:https://www.haomeiwen.com/subject/dnmsxctx.html