java线程池详解

为什么要使用线程池

ExecutorService利用池化线程执行任务,Executors的工厂方法可以创建线程池

线程池解决的问题:

  1. 减少每个线程创建消耗的时间,复用线程
  2. 提供管理线程,资源的边界

为了适应不同的业务需求,ExecutorService提供了很多可调节的参数和扩展机制,如下:

  1. 核心线程数和最大线程数
    ThreadPoolExecutor将会根据核心线程数和最大线程数自动调节线程数大小,当一个新任务被提交的时候如果线程池线程数小于核心线程数,即使这个时候其他线程是空闲的,新的线程也将被创建去处理请求,如果线程池线程数大于核心线程数小于最大线程数,那么只有当任务队列已满的情况下,新的线程才会被创建。如果创建的核心线程数和最大线程数相同,你可以创建一个具有固定线程数的线程池,如果设置最大线程数为一个非常大的数,例如Integer.MAX_VALUE,那么线程池讲允许无限创建线程去执行并发任务。通常情况下,核心线程数和最大线程数在构造线程池的时候就已经确定,但是可以通过setCorePoolSize和setMaximumPoolSize进行调节

  2. 按需构造
    默认情况下,即使是核心线程也是在任务到达的时候才会被初始化,但是如果你使用了 prestartCoreThread 或者prestartAllCoreThreads,那么可以构造一个非空的线程池

  3. 创建新的线程
    线程通过ThreadFactory被创建,如果构造的时候不指定ThreadFactory,那么默认为Executors.defaultThreadFactory。defaultThreadFactory创建的线程的线程组相同,线程优先级为NORM_PRIORITY,并且为非守护线程状态。通过传递一个不同的ThreadFactory,你可以改变线程的名称,线程组,线程优先级,守护状态等等,如果ThreadFactory创建线程失败,executor能够继续存活但不能继续处理任务,线程应该具备modifyThread的运行时权限,如果使用池的工作线程或其他线程不具有此权限,则服务可能会降级:配置更改可能无法及时生效,并且关闭池可能仍处于可以终止但未完成的状态。

  4. 线程存活时间
    如果线程数超过核心线程数,那么超过核心线程数的线程将在空闲时间超过存活时间后消亡,这种机制保证了在线程池不活跃的收资源能够被回收,当线程池变得活跃后,新的线程又会被创建,如果存活时间设置为Integer.MAX_VALUE,那么线程将不会被回收, allowCoreThreadTimeOut(boolean)方法可以控制核心线程数是否被回收,前提是存活时间不为零

  5. 阻塞队列
    任何的阻塞队列都是被用于传递和保存任务,阻塞队列的使用和线程池的大小相关

  • 少于核心线程数的时候,Executor将不会把任务放进队列,而是直接创建新线程
  • 如果线程数超过核心线程数,那么Executor将优先将任务放进队列,而不是创建新线程
  • 如果一个请求无法被放进阻塞队列(已满),那么新的线程将会被创建,如果超过了核心线程数,那么任务将会被拒绝
  1. 队列的三个通用策略

    1. 直接传递
      直接传递默认比较好的队列是SynchronousQueue,同步队列只是传递任务,并不保存任务,当任务到来时,如果没有可用线程执行任务,会立刻创建线程去处理请求,这种策略避免了具有内部依赖得一堆请求互相锁定,直接传递要求一个无界的线程池最大线程数,以避免提交的任务走拒绝策略,换种方式说,当请求更快的时候,线程增长得也更快

    2. 无界队列
      使用无界队列将会导致,当核心线程都处于占用状态时,任务在队列中等待,由于队列无界,最大线程数不会起任何作用,超过核心线程数的线程永远不会被创建

    3. 有界队列
      有界队列配合一个明确的最大线程数可以有效防止资源耗尽,但是比较难以控制。队列大小和线程池大小可以相互权衡,使用大队列和小的线程池可以最大程度地减少CPU使用率,操作系统资源和上下文切换的开销,但是吞吐量比较低。如果任务经常阻塞,则系统可能能够为比您允许的线程更多的线程安排时间。使用小型队列通常需要较大的池大小,这使 CPU 更加繁忙,但可能会遇到不可接受的调度开销,这也会降低吞吐量。

  2. 拒绝策略:
    当执行程序关闭时,以及当Executor达到最大线程数且队列使用有限边界并饱和时,在方法 execute(Runnable) 中提交的新任务将被拒绝。在任一情况下,execute 方法都会调用其 RejectedExecutionHandler 的 RejectedExecution.rejectedExecution(Runnable, ThreadPoolExecutor) 方法。有四个预定义的处理程序策略:

    1. 默认情况下,拒绝策略是ThreadPoolExecutor.AbortPolicy,这个处理器会丢弃任务并抛出运行时异常RejectedExecutionException
    2. ThreadPoolExecutor.CallerRunsPolicy,由调用线程(提交任务的线程)处理该任务,此举会降低任务的提交速度
    3. ThreadPoolExecutor.DiscardPolicy,丢弃任务,但是不抛出异常
    4. ThreadPoolExecutor.DiscardOldestPolicy,丢弃队列最前面的任务,然后重新提交被拒绝的任务
  3. 钩子函数:

jdk自带的几种线程池

posted @ 2023-03-29 13:43  itqczzz  阅读(118)  评论(0编辑  收藏  举报