线程池面试总结

972 阅读5分钟

并发和并行

并发程序指的是两个或多个线程同时存在,如果程序程序在单核处理器上运行,那么线程之间交替的换入或换出内存,这些线程是同时存在的,每个线程都处于执行过程中的某个状态。如果程序运行在多核处理器上,那么线程可以并行执行,程序中的每个线程都将分配到一个独立的处理器核上。

举个例子:加入你创建了4个线程,但是CPU是单核的,这时并发度为4,并行度只有1。如果CPU是双核的,那么并发度还是4,并行度为2。

进程之间通信的方式

  1. 管道
  2. 消息队列
  3. 共享内存
  4. 信号量
  5. socket通信

线程和进程

  • 一个进程可以包含多个线程。
  • 不同进程间数据很难共享,同一进程下不同线程间数据可以共享。
  • 进程要比线程消耗更多的计算机资源
  • 进程间不会相互影响,一个线程挂掉可能导致整个进程挂掉。
  • 进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。

线程池的生命周期

  1. 运行(RUNNING),关闭(SHUTDOWN),停止(STOP),结束(TERMINATED)
  2. shutdown()与 shutdowNow()区别:shutdown()是一个平缓的关闭过程,线程池停止接受新的任务,同时等待已经提交的任务执行完毕,包括那些进入队列还没有开始的任务,这时候线程池处于SHUTDOWN状态;shutdownNow()是一个立即关闭过程,线程池停止接受新的任务,同时线程池取消所有执行的任务和已经进入队列但是还没有执行的任务,这时候线程池处于STOP状态。
  3. 一旦shutdown()或者shutdownNow()执行完毕,线程池就进入TERMINATED状态,此时线程池就结束了。 Executor-Lifecycle_4

线程的6种状态

线程的状态

为什么需要线程池

  • 它帮我们管理线程,避免增加创建线程和销毁线程的资源损耗
  • 提高响应速度。 如果任务到达了,相对于从线程池拿线程,重新去创建一条线程执行,速度肯定慢很多。
  • 重复利用。 线程用完,再放回池子,可以达到重复利用的效果,节省资源。

线程池的核心参数

  • corePoolSize:线程池核心线程数
  • maximumPoolSize:线程池最大线程数
  • keepAliveTime:非核心线程的空闲存活时间
  • unit:非核心线程存活时间的单位
  • workQueue:存放任务的阻塞队列
  • threadFactory:用于创建线程的工厂
  • handler:线程池的饱和策略

线程池任务执行流程

  • 当提交一个任务给线程池执行,会首先检测线程池中存活核心线程的数量是否小于corePoolSize,如果小于,则创建新的核心线程执行提交的任务。
  • 如果核心线程已满,则或检测任务队列workQueue是否已满,如果workQueue未满,则会将提交的任务放入队列。
  • 如果workQueue已满,则会检测线程池中存活的线程的数量是否已经达到maximumPoolSize,如果未达到maximumPoolSize,则会创建非核心线程执行提交的任务。
  • 如果已经达到maximumPoolSize,则会执行拒绝策略。

四种拒绝策略

  • AbortPolicy:抛出异常
  • DiscardPolicy:直接丢弃任务
  • DiscardOldestPolicy:丢弃任务队列中最老的任务
  • CallerRunsPolicy:交任务交给调用线程池的线程去执行

线程池的任务队列

  • ArrayBlockingQueue:数组实现的有界阻塞队列,先进先出
  • LinkedBlockingQueue:基于链表实现的队列,先进先出,不设置容量的情况下,将是一个无界的阻塞队列,最大的长度为Integer.MAX_VALUE。
  • DelayQueue:延时队列,根据指定执行时间从小到大排序,否则根据插入到队列的先后顺序。newScheduledThreadPool中使用该队列。
  • PriorityBlockingQueue:具有优先级的无界阻塞队列
  • SynchronousQueue:同步队列,每个插入操作必须等待列一个线程的移除操作,否则插入操作一直处于阻塞状态,newCachedThreadPool中使用该队列。

几种常用的线程池

newFixedThreadPool, 即适用执行长期的任务。

  • 核心线程数和最大线程数大小一样
  • 没有非核心线程的空闲存活时间
  • 阻塞队列为无界队列LinkedBlockingQueue
  • 问题:使用无界队列的线程池可能会导致内存飙升。

newCachedThreadPool, 用于并发执行大量短期的小任务。

  • 核心线程数corePoolSize是为0
  • 最大线程数maximumPoolSize为Integer.MAX_VALUE
  • 非核心线程的空闲时间keepAliveTime为60秒
  • 阻塞队列使用SynchronousQueue
  • 问题:当任务提交的速度大于处理任务的速度时,回导致创建过多的线程,从而耗尽CPU和内存资源。

newSingleThreadExecutor, 适用于串行执行任务的场景。

  • 核心线程数corePoolSize为1
  • 最大线程数MaximumPoolSize也为1
  • 阻塞队列采用无界队列LinkedBlockingQueue
  • 非核心线程的空闲时间keepAliveTime为0

newScheduledThreadPool, 适用于周期性执行任务的场景

  • 最大线程数MaximumPoolSize为Integer.MAX_VALUE
  • 核心线程corePoolSize由自己设置,一般设置为1
  • 非核心线程的空闲时间keepAliveTime为0
  • scheduleAtFixedRate():按某种速率周期执行
  • scheduleWithFixedDelay():在某个延迟后执行
  • 阻塞队列使用DelayedWorkQueue