【Java核心知识】线程池相关知识

文章目录

  • 线程池
    • 四种常见的线程池
    • 线程池标准创建方式
    • 实现原理
    • 拒绝策略
    • 线程回收
    • 参考链接

线程池

四种常见的线程池

  • newSingleThreadExecutor 创建单线程化线程池:只有一个线程,缺点是阻塞队列没有限制,容易OOM
  • newFixedThreadPool 创建固定数量的线程池:拥有固定的线程,缺点是阻塞队列没有限制,容易OOM
  • newCachedThreadPool 创建可缓存线程池:根据提交任务动态创建线程,缺点是线程没有数量限制,容易消耗系统资源
  • newScheduledThreadPool 创建可调度线程池:每隔一定时间才会执行下一个任务

但一般不会使用这四种常见的线程池,原因在于FixedThreadPoolSingleThreadPool,允许的缓存队列的长度都是Integer.MAX_VALUE,缓存队列会保存许多待完成的任务,最终存储队列过大会导致OOM

CachedThreadPoolnewScheduledThreadPool允许的最大线程数是Integer.MAX_VALUE,线程数过多也会导致OOM

线程池标准创建方式

  • corePoolSize: 核心线程数,即使线程空闲(Idle),也不会回收。
  • maximumPoolSize: 线程数的上限
  • keepAliveTime: 线程最大空闲(Idle)时长,超过此时长的非核心线程将被销毁
  • BlockingQueue workQueue: 任务的排队队列
  • ThreadFactory threadFactory: 新线程的产生方式
  • RejectedExecutionHandler handler: 拒绝策略

当线程池中线程小于核心线程数时,每提交一个任务都会创建一个线程,无论是否有空闲线程;

如果线程数已经等于核心线程数,如果排队队列还没满,提交新任务会放入队列中;如果对列已满,则会创建非核心线程,只有排队队列满了之后才会创建新的线程。

实现原理

线程池内部记录了核心线程数最大线程数这两个变量,同时持有一个阻塞队列,运行过程中,线程会不断地尝试从阻塞队列中获取任务,如果获取到任务就执行,否则阻塞。

当有任务进来时,

  • 如果当前线程数小于核心线程数,则会创建线程来执行任务;
  • 如果当前线程数大于核心线程数且阻塞队列没有满,则将任务放入阻塞队列中;
  • 如果阻塞队列满了但线程数小于最大线程数,则创建新的线程执行任务;
  • 如果线程数超过了最大线程数,则需要采用拒绝策略进行拒绝;

拒绝策略

拒绝策略采用了策略设计模式,常见的拒绝策略有:

  • AbortPolicy:拒绝并抛出异常,生产中常用。
  • DiscardPolicy:抛弃任务,但不产生异常。
  • DiscardOldestPolicy:抛弃最老任务,但不产生异常。
  • CallerRunsPolicy:不会产生新的线程,由调用者执行任务。

线程回收

当线程数超过核心线程后,这些多余的线程如何回收呢?答案是源码中线程执行时会调用getTask()方法尝试从队列中获取任务,如果获取到的任务为null,则会进行线程回收。当然还需要判断当前活跃的线程数,当线程数等于核心线程数之后,就不会回收线程了。

参考链接

线程池中多余的线程是如何回收的

最全八股文的线程池长文预警

你可能感兴趣的:(#,Java核心知识,java,开发语言,面试)