• 线程池的使用


    Java知识点总结:想看的可以从这里进入

    9.2.3、使用线程池

    线程池是一种线程使用模式,为了应对频繁的创建和销毁线程会带来调度开销,影响缓存局部性和整体性能而创建。线程池内可以维护多个线程,不需要频繁和销毁,而是统一进行管理,只需要管理者分配可并发执行的任务即可,避免了在处理短时间任务时创建与销毁线程的代价。

    线程池的原理和数据库连接池是类似的,为了能够复用线程、限制并发数以及减少上下文切换带来的资源消耗,使用池化的技术对线程进行管理。

    线程池是事先创建若干个可执行的线程放入一个池中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。线程池做的工作主要是控制运行的线程的数量,处理过程中将任务加入队列,然后在线程创建后启动这些任务,如果线程数超过了最大数量,超出的数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。

    jdk给我们提供了Executor框架、Executors工具类,来创建和使用线程池,架构图如下:

    在这里插入图片描述

    ​ 1. 通过 ThreadPoolExecutor 创建的线程池

    ​ 2. 通过 Executors 创建的线程池

    1、Executor框架
    • Executor接口:只有一个待实现的方法,用来执行一项Runnable任务

      在这里插入图片描述

    • ExecutorService接口:对Executor进行了扩展,提供了更多的方法,一般使用它来代替Executor

      在这里插入图片描述

    • ThreadPoolExecutor:Executor框架的正真实现者,实现了execute方法

      private final HashSet<Worker> workers = new HashSet<>();		//Worker集合,线程池
      private final BlockingQueue<Runnable> workQueue;	//阻塞队列,要执行的任务
      final void runWorker(Worker w);		//Worker调用这个方法,可以从阻塞队列中获取任务来执行。
      
      • 1
      • 2
      • 3

      其构造函数接收的参数:

      1public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
      							BlockingQueue<Runnable> workQueue) {}
      2public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
                                    BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory) {}
      3public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
                                    BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler) {}
      4public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
                                    BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,
                                    RejectedExecutionHandler handler) {}
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      1. corePoolSize:核心线程数,也即最小的线程数,线程池会维护一个最小的线程数。

      2. maximumPoolSize:最大的线程数

      3. keepAliveTime:线程的存活时间。默认情况下,只有当线程池中的线程数大于corePoolSize时,才会起作用,直到线程池中的线程数不大于corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;

      4. unit:存活时间的单位

        TimeUnit.DAYS; //天
        TimeUnit.HOURS; //小时
        TimeUnit.MINUTES; //分钟
        TimeUnit.SECONDS; //秒
        TimeUnit.MILLISECONDS; //毫秒
        TimeUnit.MICROSECONDS; //微妙
        TimeUnit.NANOSECONDS; //纳秒

      5. workQueue:存放任务的阻塞队列,使用阻塞队列可以管理线程的组合和唤醒。

        ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
        LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于数组的
        SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于 LinkedBlockingQuene;
        priorityBlockingQuene:具有优先级的无界阻塞队列;

      6. threadFactory:创建线程的线程工厂

        Executors.defaultThreadFactory():工具类的默认工厂

      7. handler:线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:

        • new ThreadPoolExecutor.AbortPolicy(默认,中止策略):丢弃任务,抛出

          RejectedExecutionException异常阻止系统正常运行。

        • new ThreadPoolExecutor.CallerRunsPolicy(回退策略):将提交的任务会退给调用线程,让调用线程执行。

        • new ThreadPoolExecutor.DiscardOldestPolicy(抛弃最老策略):丢弃队列最前面的任务,并将新任务加入

        • new ThreadPoolExecutor.DiscardPolicy(抛弃策略):丢弃任务,但是不抛出异常。

      线程池监控API:

      • getActiveCount() :获取线程池中正在执行任务的线程数量
      • getCompletedTaskCount():获取已执行完毕的任务数
      • getTaskCount() :获取线程池已执行与未执行的任务总数
      • getPoolSize():获取线程池当前的线程数
      • getQueue().size():获取队列中的任务数
    • 如果线程池中的线程数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
    • 如果线程池中的线程数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列。
    • 如果线程池中的线程数量大于等于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
    • 如果线程池中的线程数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过handlerf所指定的策略来处理此任务。
    • 当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数
    2、Executors工具类

    Executors它是一个线程工具类,内部封装的方法能帮助我们创建线程池:

    在这里插入图片描述

    **注意:**阿里的开发手册中是禁止使用Executors的

    虽然Exectors工具类给我们提供了一些方法,可以方便的获取一些配置好的ThreadPoolExecutor类,但是它们的配置有一些不合理的地方,在实际生产中,我们一般会自定义线程池,而不是使用jdk提供的。以下是阿里中的规定:

    【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
    说明:Executors返回的线程池对象的弊端如下:
    1)FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAx_vALUE,可能会堆积大量的请求,从而导致ooM。
    2)CachedThreadPool和ScheduledThreadPool:允许的创建线程数量为snteger.MAx_vALUE,可能会创建大量的线程,从而导致ooM。
    
    • 1
    • 2
    • 3
    • 4
    3、创建线程池
    Executors

    在这里插入图片描述

    • 创建固定容量的线程池

      在这里插入图片描述

      在这里插入图片描述

    • 创建单个线程的线程池

      在这里插入图片描述

      在这里插入图片描述

    • 容量可变的线程池
      在这里插入图片描述
      在这里插入图片描述

    ThreadPoolExecutor

    Executors工具类创建的线程池,本质上也是构建ThreadPoolExecutor类,所以我们也可以直接根据ThreadPoolExecutor类的四个构造方法去创建线程。

    public ThreadPoolExecutor(int corePoolSize,		//最小线程数
                              int maximumPoolSize,	//最大线程数
                              long keepAliveTime,	//线程没有任务执行时最多保持多久时间会终止。
                              TimeUnit unit,		//存活时间
    						  BlockingQueue<Runnable> workQueue) {}	//存放任务的阻塞队列
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {}	//线程工厂
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {}	//线程池的饱和策略
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

  • 相关阅读:
    2小时开发《点球射门游戏》,动画演示思路(上),代码已开源
    1020. 飞地的数量
    匿名用户上传的Mybatis学习笔记,炸来了阿里P8,网上一片好评
    【2022年高教杯数学建模】C题:古代玻璃制品的成分分析与鉴别方案及代码实现(一)
    springcloud
    axios调用springboot项目接口获取数据简述版
    训练神经网络的各种优化算法【文末赠书】
    Oracle根据主键获取对应表和修改序列器当前值
    【算法】链表常见算法3
    springcloud-config配置中心
  • 原文地址:https://blog.csdn.net/yuandfeng/article/details/126559143