线程池概述


线程池概述

一种池化技术,减少线程频繁创建/销毁带来的性能开销

线程池优点:
降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。

提高响应速度:任务到达时,无需等待线程创建即可立即执行。

提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。

提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。

Executor

接口Executor提供了一种思想:将任务提交和任务执行进行解耦。用户无需关注如何创建线程,如何调度线程来执行任务,用户只需提供Runnable对象,将任务的运行逻辑提交到执行器(Executor)中,由Executor框架完成线程的调配和任务的执行部分

线程池在内部实际上构建了一个生产者消费者模型,将线程和任务两者解耦,并不直接关联,从而良好的缓冲任务,复用线程。线程池的运行主要分成两部分:任务管理、线程管理; 内部通过维护一个阻塞队列存放任务,然后由线程池去自主获取任务。

线程池内部使用一个变量维护两个值:运行状态(runState)和线程数量 (workerCount)

线程池参数

	直接new ThreadPoolExecutor()对象,并且手动的指定对应的参数
     corePoolSize:线程池的核心线程数量 线程池创建出来后就会 new Thread() 5个
     maximumPoolSize:最大的线程数量,线程池支持的最大的线程数
     keepAliveTime:存活时间,当线程数大于核心线程,空闲的线程的存活时间 8-5=3
     unit:存活时间的单位
     BlockingQueue<Runnable> workQueue:阻塞队列 当线程数超过了核心线程数据,那么新的请求到来的时候会加入到阻塞的队列中
     new LinkedBlockingQueue<>() 默认队列的长度是 Integer.MAX 那这个就太大了,所以我们需要指定队列的长度
     threadFactory:创建线程的工厂对象
     RejectedExecutionHandler handler:当线程数大于最大线程数的时候会执行的淘汰策略
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5
                , 100
                , 10
                , TimeUnit.SECONDS
                , new LinkedBlockingQueue<>(10000)
                , Executors.defaultThreadFactory()
                , new ThreadPoolExecutor.AbortPolicy()
        );

线程池执行流程

  • 首先根据 corePoolSIze创建工作线程
  • 当并发量超过核心线程数,将多余的任务放如任务队列
  • 如果任务队列满了,判断并发量是否超过最大线程数量,如果没有创建新的线程执行任务;
  • 当任务处理完成,超过核心线程数量的线程会根据TimeUtil,过期销毁
  • 如果启动新线程后仍处理不了任务,多余的任务根据拒绝策略进行其他操作

JDK中提供的几种线程池

  1. newCachedThreadPool
    带有缓存的线程池,会在需要线程资源的时候进行创建,注意这里的最大线程数是Integer.MAX_VALUE
    线程休眠时间为60s,超过时间会进行销毁。
    一般用于执行短生命周期的异步任务
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
  1. newSingleThreadExecutor
    单例线程池,线程池中时刻保证工作线程为1, 串行执行任务。
    不同于fixedPool, 他保证返回的Executor不会创建额外的线程
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
   return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>(),
                                threadFactory));
}
  1. newScheduledThreadPool
    具有定时任务的线程池,能够在延迟一定时间或定期执行任务。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
  return new ScheduledThreadPoolExecutor(corePoolSize);
}


// new ScheduledThreadPoolExecute()
// 创建一个延迟队列
public ScheduledThreadPoolExecutor(int corePoolSize,
                               ThreadFactory threadFactory) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue(), threadFactory);
}

二、线程池设计

  1. 任务调度
        任务的来源一般为两个:第一种是任务直接被线程进行处理;第二种是核心线程已满,将后续的任务先放入阻塞队列,等待线程空闲再从队列中获取。
        任务缓冲就是这里的阻塞队列。当阻塞队列,最大线程数都到达极限的时候,会根据拒绝策略保证线程池的安全。
public interface RejectedExecutionHandler {
   void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
  1. 线程管理
    线程池中使用Worker维护线程管理,实现了Runnable
private final class Worker extends AbstractQueuedSynchronizer implements Runnable{
    final Thread thread;//Worker持有的线程
    Runnable firstTask;//初始化的任务,可以为null
}

thread为工作线程,使用ThreadFactory进行创建; firstTask为任务列表,如果firstTask不为空,说明当前线程可以直接处理任务,否则会创建一个队列,等待非核心线程创建去处理。

这里的Worker实现了AQS,使用不可重入的特性去反应线程现在的执行状态。

  1. lock方法一旦获取了独占锁,表示当前线程正在执行任务中。

  2. 如果正在执行任务,则不应该中断线程。

  3. 如果该线程现在不是独占锁的状态,也就是空闲的状态,说明它没有在处理任务,这时可以对该线程进行中断。

  4. 线程池在执行shutdown方法或tryTerminate方法时会调用interruptIdleWorkers方法来中断空闲的线程,interruptIdleWorkers方法会使用tryLock方法来判断线程池中的线程是否是空闲状态;如果线程是空闲状态则可以安全回收。

  1. 线程的增加 addWorker, 参数firstTast, core, 表示新创建的线程执行的第一个任务,可以为空; core为booelan类型标识当前线程是否为核心线程。

该方法只用来创建一个新的线程,具体的调用逻辑由上层实现。

  1. 线程的销毁,线程池中资源的管理由JVM进行垃圾回收,在线程池内部会通过一个hashMap维护引用,当销毁的时候只要消除引用即可。

由于引起线程销毁的可能性有很多,线程池还要判断是什么引发了这次销毁,是否要改变线程池的现阶段状态,是否要根据新状态,重新分配线程。

  1. Worker工作流程:线程中的run() 方法会执行具体的工作流程,首先判断当前是否有任务,如果没有,执行销毁线程的逻辑(如果小于核心线程会保留),否则利用锁机制保证执行任务。
    在这里插入图片描述

三、 线程池的应用

3.1 快速响应请求

在访问一个网页的时候伴随许多资源的加载,此时可以使用多线程将资源(图片,文字,视频等)并发的执行。
而为了追求快速响应,不应该设置缓冲队列,而是调高核心线程数量和最大线程数量。

参考文章

Java线程池实现原理及其在美团业务中的实践

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值