• JAVA线程池


    1、BlockingQueue

    本身是一个Queue(队列) ,它还支持在检索元素时等待队列变为非空,并在存储元素时等待队列中的空间变为可用的操作。
    BlockingQueue方法有四种形式,用不同的方式处理不能立即满足但可能在未来某个时间点满足的操作:一种抛出异常,第二种返回特殊值( null或false ,取决于操作),第三个无限期地阻塞当前线程,直到操作成功,第四个阻塞只有给定的最大时间限制才放弃。下表总结了这些方法:

    抛出异常特殊价值超时
    插入add(e)offer(e)put(e)offer(e, time, unit)
    消除remove()poll()take()poll(time, unit)
    检查(判断队列首)element()peek()不适用不适用

    BlockingQueue不接受null元素。在尝试add 、 put或offer null时,实现会抛出NullPointerException 。 null用作标记值以指示poll操作失败。
    BlockingQueue是容量受限的。在任何给定时间,它都可能有一个remainingCapacity(指定大小后的剩余)容量,超出该容量就不能put不阻塞的情况下放置其他元素没有任何内在容量限制的BlockingQueue总是报告Integer.MAX_VALUE的剩余容量(会导致oom)
    BlockingQueue实现主要用于生产者-消费者队列。
    BlockingQueue实现是线程安全的。所有排队方法都使用内部锁或其他形式的并发控制以原子方式实现其效果。然而,批量Collection 操作addAll 、 containsAll 、 retainAll和removeAll不一定以原子方式执行,除非在实现中另有指定。因此,例如,在仅添加c中的一些元素后addAll(集合)可能会失败(抛出异常)。
    JDK中七个队列:
    1、ArrayBolckingQueue(常用):基于数组的有界阻塞队列(在初始化的时候,必须传入一个容量大小的值)
    2、LinkedBlockingQueue(常用):基于链表的有界阻塞队列 大小默认为 Integer最大值
    3、PriorityBlockingQueue : 一个支持优先级排序的无界阻塞队列。
    4、DelayQueue: 使用优先级队列实现的延迟无界阻塞队列
    5、SynchronousQueue: 一个不存储元素的阻塞队列。
    6、LinkedTransferQueue: 一个由链表结构组成的无界阻塞队列。
    7、LinkedBlockingDeque: 一个由链表结构组成的双向阻塞队列

    2、Java线程池的7个参数

    1、corePoolSize - 保留在池中的核心线​​程数,即使它们是空闲的,除非设置allowCoreThreadTimeOut(boolean)(所有核心线程超时时间设置后,到达指定时间后会关闭,否则一直存活)
    2、maximumPoolSize – 池中允许的最大线程数
    3、keepAliveTime – 当线程数大于核心时,这是多余的空闲线程在终止前等待新任务的最长时间。
    4、unit – keepAliveTime参数的时间单位
    5、workQueue – 用于在执行任务之前保存任务的队列。此队列将仅保存由execute方法提交的Runnable任务。(一般需要我们根据业务需求指定大小)
    6、threadFactory – 执行器创建新线程时使用的工厂
    7、handler – 由于达到线程边界和队列容量而阻塞执行时使用的处理程序
    在这里插入图片描述

    在这里插入图片描述

    3、使用Executors类中定义的线程池

    JUC包中定义的Executor 、 ExecutorService 、 ScheduledExecutorService 、 ThreadFactory和Callable类的工厂和实用程序方法。该类支持以下几种方法:
    1、创建和返回ExecutorService的方法设置了常用的配置设置。
    2、创建和返回具有常用配置设置的ScheduledExecutorService的方法。
    3、创建和返回“包装”的 ExecutorService 的方法,通过使特定于实现的方法无法访问来禁用重新配置。
    4、创建和返回将新创建的线程设置为已知状态的ThreadFactory的方法。
    5、从其他类似闭包的形式中创建和返回Callable的方法,因此它们可以在需要Callable的执行方法中使用。
    newFixedThreadPool:一池多线程(该线程池重用在共享无界队列上运行的固定数量的线程)
    newSingleThreadExecutor:一池一线程(在无界队列上运行的 Executor,与其他等效的newFixedThreadPool(1)不同,返回的执行程序保证不可重新配置以使用额外的线程)
    newCachedThreadPool:缓存线程。(这里与自定义线程类似,不同在于设置队列是固定的Integer最大值,会导致oom)
    **注:**阿里规约中不建议使用Executors类创建的线程池,这样会导致我们没法清楚知道线程支援的分配,这里只做示范
    在这里插入图片描述

    4、线程池的工作原理

    当我们new ThreadPoolExecutor()对应线程池后并不会执行,线程池真正执行是要调线程池中execute(Runnable command)方法。主要工作流程为:
    1、初始化如下图核心线程数为2,总线程为5个
    2、当程序调用execute方法,调用线程池
    3、首先将任务交给核心线程去执行
    4、当核心线程数拿满之后,再有任务进来,此时将任务(runnable.run方法内业务)放入阻塞队列中(阻塞队列大小为4)
    5、当阻塞队列达到最大值时,此时不能再添加等待任务,新的任务直接被非核心线程领走运行
    6、当非核心线程也运行满时,此时就选择对应配置的淘汰策略
    AbortPolicy中止策略:丢弃任务并抛出RejectedExecutionException异常。(比较关键的业务,推荐使用此拒绝策略)

    DiscardPolicy丢弃策略:ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。(如果你提交的任务无关紧要,你就可以使用它)

    CallerRunsPolicy调用者运行策略:由调用线程处理该任务。(只要线程池没有关闭,就由提交任务的当前线程处理,谁提交的任务就交给提交的线程去处理)

    DiscardOldestPolicy弃老策略:丢弃队列最前面的任务,然后重新提交被拒绝的任务。(如果线程池未关闭,就弹出队列头部的元素,然后尝试执行。一般在不允许失败的、对性能要求不高、并发量较小的场景下使用,因为线程池一般情况下不会关闭,也就是提交的任务一定会被运行,但是由于是调用者线程自己执行的,当多次提交任务时,就会阻塞后续任务执行,性能和效率自然就慢了)

    在这里插入图片描述
    了解线程工作原理后,我们可以根据我们具体的业务需求自定义对应的线程池!

  • 相关阅读:
    【C++】设计模式
    【Win10】如何关闭Windows10自动更新
    XILINX XC7A200T-1FBG676C FPGA - 现场可编程门阵列
    可视化大屏:mapbox+vue全攻略
    他做国外LEAD,用了一年时间,把所有房贷都还清了
    Java基础之线程的那点事
    最短路算法 - dijkstra
    2023大厂高频面试题之Vue篇(3)
    Ubuntu 系统安装 VS Code 并配置 C++ 环境
    【FIW2022精彩回顾】数据库领域资深专家韩锋:金融行业数据库自主创新之路
  • 原文地址:https://blog.csdn.net/wuhui2100/article/details/126009098