• JDK线程池的总结


    1. 概述

    1.1 背景/引入

    线程是系统资源,每创建新的线程会占用资源,且创建和销毁对象开销较大;
    高并发时,如果为每个任务都创建新的线程,则对内存占用太大,可能会内存溢出;
    线程池技术的引入,就是为了解决这一问题.

    1.2 什么是线程池

    在这里插入图片描述

    一个线程集合用来控制线程的数量,会先创建一定数量的线程放入空闲队列中,当任务来了之后就给任务分配线程去执行;
    当线程被任务占满后,会将任务放到阻塞队列排队,等待其他线程执行完毕,再从队列中取出任务来执行;
    如果选的是有界队列,当阻塞队列满了,再来新的任务,就会创建救急线程;
    当救急线程也满了,会执行拒绝策略;

    池化技术的思想主要是为了减少每次获取资源的消耗,提⾼对资源的利用率。

    1.3 优点

    降低资源消耗:通过重复利⽤已创建的线程降低线程创建和销毁造成的消耗,提高线程的重用性。
    提高响应速度:当任务到达时,任务可以不需要的等到线程创建就能⽴即执⾏。
    提高线程的可管理性:线程是稀缺资源,如果⽆限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使⽤线程池可以进⾏统⼀的分配,调优和监控。

    1.4 类图

    在这里插入图片描述
    ExecutorService是线程池的基本接口,提供了提交任务、关闭线程池的方法;
    ScheduledExecutorSerivce是ExecutorService的子接口,在ExecutorService基础上新增了任务调度的功能,可以定时执行任务;
    ThreadPoolExecutor是ExecutorService的基础实现类;
    ScheduledThreadPoolExecutor继承于ThreadPoolExecutor, 是带任务调度功能的实现类;

    2. 线程池状态

    在这里插入图片描述

    3. ThreadPoolExecutor构造(7大参数)

    在这里插入图片描述

    参数说明:

    在这里插入图片描述

    由于构造方法参数太多,初学者不易掌握,所以有Executor工具类提供了工厂方法来创建线程池;

    工厂方法:

    1. newFixedThreadPool(int nThreads):创建固定大小的线程池,并直接返回线程池对象;

    在这里插入图片描述
    在这里插入图片描述
    特点:
    1.参数线程数n是核心线程数,救急线程数为0 ;
    2.阻塞队列是无界的,可以放任意数量的任务(救急线程的前提是选择了有界队列)

    1. newSingleThreadPool():创建单线程线程池
      返回的是装饰器,只有ExecutorService的方法,不能使用ThreadPoolExecutor的方法;
      使用场景:
      希望任务排队串行执行;线程数固定为1,任务数多余1时,会放入无界队列排队;
      和自己创建一个任务的区别:
      自己创建的一个线程如果任务失败了没有任何补救措施,而线程池还会创建新的线程,保证线程池的正常工作

    运行流程:

    execute() 添加任务到线程池执行,当核心线程数不够时,则将任务放到阻塞队列中排队:
    当任务超过了阻塞队列大小时,再来任务,就会创建maximumPoolSize - corePoolSize 个的救急线程来运行任务(前提是选择有界队列);
    救急线程的前提是选择了有界队列;若队列是无界队列,即没有容量限制的队列,则不会创建救急线程;

    救急线程和核心线程的区别 ?

    救急线程有生存时间,类似临时工,任务完毕救急线程就被销毁;而核心线程执行完任务依然保留在线程池中;

    当线程到达maximumPoolSize最大线程数仍有新任务时,即核心线程和救急线程都被任务占完了,才会执行拒绝策略;

    4. 阻塞队列

    阻塞队列一般用于生产者、消费者场景,生产者往队列中添加元素,消费者从队列取线程;
    在线程池中,通过阻塞队列来实现任务的生产和消费功能;
    当队列为空,会阻塞消费者;当队列满了,会阻塞生产者;

    阻塞队列有一个非常重要的属性,那就是容量的大小,分为有界无界两种。

    通常,阻塞队列是有界的,例如 ArrayBlockingQueue 如果容量满了,也不会扩容,所以一旦满了就无法再往里放数据了。(救急线程的前提是选择有界队列 !)
    无界队列意味着里面可以容纳非常多的元素,例如 LinkedBlockingQueue 的上限是 Integer.MAX_VALUE,约为 2 的 31 次方,是非常大的一个数,可以近似认为是无限容量,因为我们几乎无法把这个容量装满。

    缺点:无界队列存在比较大的潜在风险,如果在并发量较大的情况下,线程池中可以几乎无限制的添加任务,容易导致内存溢出的问题!

    5. JDK线程池的拒绝策略

    在这里插入图片描述
    AbortPoliocy:抛出异常(默认)
    CallerRunsPolicy:让调用线程(提交任务的线程)直接执行此任务
    DiscardPolicy:放弃本次任务
    DiscardOldestPolicy:放弃最早的任务,当前任务取而代之

    6. 提交任务

    1. execute:
      参数是Runable类型,无返回结果;
      在这里插入图片描述

    2. submit:
      参数是Callable,实现Callable的线程是有返回值的;
      submit方法会返回Future对象,可以用来判断任务是否执行成功;
      FutureTask用来在主线程中接收线程池中返回的结果;调用FutureTask对象的**get()**方法获取返回值;
      在这里插入图片描述

    3. invokeAll:
      接收一个集合,执行集合中所有的任务,
      返回LIst集合,集合中是Future,
      在这里插入图片描述

    4. invokeAny:
      不会执行所有任务,而是找到最先执行的任务;
      只要集合中有一个任务执行成功,就把整个任务的结果作为返回最终的结果返回,其他的取消;在这里插入图片描述

    7. 关闭线程池

    1. shutdown:
      不会接收新的任务,但会处理阻塞队列中剩余任务

    2. shutdownNow:
      将线程池的状态变成stop,即不仅不接受新任务,还会将正在执行的任务停下来 !
      正在执行的任务会用interrupt方法打断 ! 并将任务返回
      在这里插入图片描述

  • 相关阅读:
    Mybatis 12
    Metabase学习教程:视图-6
    Redis分布式锁
    股票跌幅榜查询易语言代码
    [回文串][贪心]leetcode6166:最大回文数字(medium)
    小张刷力扣--第二十二天
    安装typescript环境并开启VSCode自动监视编译ts文件为js文件
    App线上网络问题优化策略
    从字节码角度带你彻底理解i++与++i
    C++11新特性nullptr
  • 原文地址:https://blog.csdn.net/Swofford/article/details/125890432