跳到主要内容

线程池

什么是线程池

  • 线程池就是创建若干个可执行的线程放入一个池(容器)中,有任务需要处理时,就会提交到线程池中的任务队列,处理完之后线程并不会被销毁,而是仍然在线程池中等待下一个任务
  • 线程池实际上是一种 生产者-消费者模式

为什么使用线程池

  • 在 Java 中创建一个线程需要调用操作系统内核的 API,操作系统需要为线程分配一系列资源,成本很高,因此线程是一个重量级对象,应该避免频繁创建和销毁,使用线程池就可以很好的避免频繁的创建和销毁
  • 线程池的优点
    • 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗
    • 提高响应速度:当任务到达时,任务可以不需要等待线程创建就可以立刻执行
    • 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统稳定性,使用线程池可以进行统一的分配、调优和监控

线程池的参数

  1. 线程池核心线程大小 - corePoolSize
    线程池中会维护一个最小的线程数量,即使这些线程处于空闲状态也不会被销毁,除非设置了 allowCoreThreadTimeOut
    此处最小线程数量即 corePoolSize,任务提交到线程池后,首先会检查当前线程数是否达到了 corePoolSize,没有达到则会创建一个新线程来处理任务
  2. 线程池最大线程数量 - maximumPoolSize
    当前线程数达到 corePoolSize 后,如果后续有任务被提交到线程池,会将任务缓存到工作队列中,如果队列也已满,则会创建一个新线程来处理这个任务
    但是线程池不会无限制的创建新线程,而是有一个最大线程数量的限制,这个数量即由 maximumPoolSize 指定
  3. 空闲线程存活时间 - keepAliveTime
    一个线程如果处于空闲状态且当前线程数量大于 corePoolSize,那么在指定时间后这个空闲线程就会被销毁,这个时间由 keepAliveTime 指定
  4. 空闲线程存活时间单位 - unit
    keepAliveTime 的计量单位,Timeunit
  5. 工作队列 - workQueue
    新任务被提交之后会先进入此工作队列中,任务调度时再从队列中取出任务
    JDK 提供了四种工作队列
    • ArrayBlockingQueue
      基于数组的有界阻塞队列,按 FIFO 排序。
      新任务进来后会放在该队列的队尾,有界的数组可以防止资源耗尽问题,当线程池中线程数量达到 corePoolSize 后,再有新任务进来,则会将任务放入该队列的队尾等待被调度,如果队列是满的,就会创建一个新线程执行任务,此时如如果线程数量已经达到 maximumPoolSize 则会执行拒绝策略
    • LinkedBlockingQueue
      基于链表的无界阻塞队列(其实最大容量为 Integer.MAX),按照 FIFO 排序。
      由于该队列的近似无界性,当线程池中线程数量达到 corePoolSize 后,再有新任务进来,会一直存入该队列,而基本不会去创建新线程,知道 maximumPoolSize(基本不可能达到 Integer.MAX 这个数值),因此使用该工作队列时,参数 maximumPoolSize 实际上是不起作用的
    • SynchronousQueue
      一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。
      即当新任务进来时不会缓存,而是直接被调度执行该任务,如果没有可用线程,则会创建新线程,如果线程数量达到 maximumPoolSize,则执行拒绝策略
    • PriorityBlockingQueue
      具有优先级的无界阻塞队列,优先级通过参数 Comparator 实现
  6. 线程工厂 - threadFactory
    创建一个新线程时使用的工厂,可以用来设定线程名、是否为 daemon 线程等
  7. 拒绝策略 - handler
    当工作队列中的任务已经达到最大限制,并且线程池中的线程数量也达到最大限制时,此时如果有新任务提交进来,则会使用以下的拒绝策略进行处理
    • CallerRunsPolicy
      在调用者线程中直接执行被拒绝任务的 run 方法,如果线程池已经 shutdown,则直接抛弃任务
      CallerRunsPolicy
    • AbortPolicy
      直接丢弃任务,并抛出 RejectedExecutionException 异常
      AbortPolicy
    • DiscardPolicy
      直接丢弃任务,什么都不做
      DiscardPolicy
    • DiscardOldestPolicy
      抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列
      DiscardOldestPolicy

线程池的种类

  • newCachedThreadPool
    创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。
    对于执行很多短期异步任务的程序而言,这些线程通常可提高程序性能,调用 execute 将重用以前构造的线程(如果线程可用),如果现有线程没有可用的,则创建一个新线程并添加到池中。
    会终止并从缓存中移除那些已有 60 秒钟未被使用的线程,因此长时间保持空闲的线程池并不会使用任何资源
  • newFixedThreadPool
    创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程,大多数的线程会处于处理任务的活动状态。
    如果在所有线程处于活动状态是提交任务,则在有可用线程前,任务将在队列中等待,如果在关闭前的执行期间由于失败而导致任何线程终止,那么新线程将代替它执行后续任务(如果需要)。
    某个在线程被显式关闭前,将会在池中一直存在
  • newScheduledThreadPool
    创建一个线程池,它可以安排在给定的延迟时间后执行任务或者定时的执行
  • newSingleThreadExecutor
    返回一个只有一个线程的线程池,这个线程池可以在线程死后(或者发生异常时)重新启动一个线程来代替原有线程继续执行