• Java线程池


    Java的线程池,各个参数的作用,如何进行的?

    1.线程池核心参数

    public ThreadPoolExecutor(
      int corePoolSize,
    int maximumPoolSize,
    long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

    线程池可以通过ThreadPoolExecutor来创建,我们来看一下它的几个核心参数:

    • corePoolSize: 线程池核心线程数最大值
    • maximumPoolSize: 线程池最大线程数大小
    • keepAliveTime: 线程池中非核心线程空闲的存活时间大小
    • unit: 线程空闲存活时间单位
    • workQueue: 存放任务的阻塞队列
    • threadFactory: 用于设置创建线程的工厂,可以给创建的线程设置有意义的名字,可方便排查问题。
    • handler: 线城池的饱和策略事件,主要有四种类型。

    核心线程和非核心线程区别:

    核心线程: 可闲置 不会被销毁  。(ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true时,keepAliveTime同样会作用于核心线程)

    非核心线程:非核心线程当闲置时间超过 keepAliveTime这个时长,非核心线程就会被回收。

    核心线程数就像是工厂正式工,最大线程数,就是工厂临时工作量加大,请了一批临时工,临时工加正式工的和就是最大线程数,等这批任务结束后,临时工要辞退的,而正式工留下。

    2.线程池创建流程

    • 提交一个任务,如果当前工作中的线程数量少于corePoolSize,就创建新的线程来执行任务。
    • 当线程池的工作中的线程数量达到了corePoolSize,新提交的任务,会被放进任务队列workQueue排队等待执行。
    • 当线程池里面存活的线程数达到corePoolSize了,并且任务队列workQueue也满,判断线程数是否达到maximumPoolSize,即最大线程数是否已满,如果没到达,创建一个非核心线程执行提交的任务。
    • 如果当前的线程数达到了maximumPoolSize,还有新的任务过来的话,直接采用拒绝策略处理。

      

    3.四种拒绝策略

    • AbortPolicy(抛出一个异常,默认的)
    • DiscardPolicy(直接丢弃任务)
    • DiscardOldestPolicy(丢弃队列里最老的任务,将当前这个任务继续提交给线程池)
    • CallerRunsPolicy(交给线程池调用所在的线程进行处理)

    4. 线程池的作用:

    池化技术应用:线程池、数据库连接池、http连接池等等。

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

    使用线程池的好处:

    • 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

    • 提高响应速度:当任务到达时,可以不需要等待线程创建就能立即执行。

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

    5.线程池中线程的生命周期

    设:我们有一个coreSize=10,maxSize=20,keepAliveTime=60s,queue=40
    1、池初始化时里面没有任何线程。
    2、当有一个任务提交到池就创建第一个线程。
    3、若继续提交任务,有空闲线程就调拨空闲线程来处理任务?若没有线程空闲则再新建一个线程来处理,如此直到coreSize。【预热阶段
    4、若继续提交任务,有空闲线程就调拨空闲线程来处理任务,如果没有空闲线程(10个)则将任务缓存到queue中排队等待。
    5、若继续提交任务,而已有线程不空闲,且queue也满了,则新建线程,并将最新的任务优先提交给新线程处理。
    6、若继续提交任务,且所有线程(20个)仍不空闲,queue也是满的,此时就会触发池的拒绝机制。
    8、一旦有任何线程空闲下来就会从queue中消费任务,直到queue中任务被消费完。
    9、当总忙碌线程个数不超过coreSize时,闲暇线程休息keepAliveTime过后会被销毁。
    10、而池中一直保留coreSize个线程存活。

    6.线程池的实现原理

    其实java线程池的实现原理很简单,说白了就是一个线程集合workerSet和一个阻塞队列workQueue。当用户向线程池提交一个任务(也就是线程)时,线程池会先将任务放入workQueue中。workerSet中的线程会不断的从workQueue中获取线程然后执行。当workQueue中没有任务的时候,worker就会阻塞,直到队列中有任务了就取出来继续执行

    线程池都有哪几种工作队列?

    1.线程池的5种工作队列

    • ArrayBlockingQueue
    • LinkedBlockingQueue
    • DelayQueue
    • PriorityBlockingQueue
    • SynchronousQueue

    任务太多的时候,工作队列用于暂时缓存待处理的任务,jdk中常见的5种阻塞队列:

    ArrayBlockingQueue(数组阻塞队列):是一个基于数组结构的有界阻塞队列,此队列按照先进先出原则对元素进行排序

    LinkedBlockingQueue(链表阻塞队列):是一个基于链表结构的阻塞队列,此队列按照先进先出排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool使用了这个队列。

    DelayQueue(延迟队列):是一个任务定时周期的延迟执行的队列。根据指定的执行时间从小到大排序,否则根据插入到队列的先后排序。newScheduledThreadPool线程池使用了这个队列。

    PriorityBlockingQueue(优先级阻塞队列):优先级队列,进入队列的元素按照优先级会进行排序。

    SynchronousQueue (同步队列):一个不存储元素的阻塞队列,每个插入操作必须等到另外一个线程调用移除操作,否则插入操作一直处理阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用这个队列。

    几种常见的线程池及使用场景

    1. 四种常用的线程池

    • newFixedThreadPool (固定数目线程的线程池)
    • newCachedThreadPool(可缓存线程的线程池)
    • newSingleThreadExecutor(单线程的线程池)
    • newScheduledThreadPool(定时及周期执行的线程池)

    a.newFixedThreadPool:

    • 通俗:创建可容纳固定数量线程的池子,每个线程的存活时间是无限的,当池子满了就不在添加线程了;如果池中的所有线程均在繁忙状态,对于新任务会进入阻塞队列中(无界的阻塞队列)
    • 适用:FixedThreadPool 适用于处理CPU密集型的任务,确保CPU在长期被工作线程使用的情况下,尽可能的少的分配线程,即适用执行长期的任务。
    • 特点:
      • 核心线程数和最大线程数大小一样
      • 没有所谓的非核心线程空闲时间,即keepAliveTime为0
      • 阻塞队列为无界队列LinkedBlockingQueue

      问:使用无界队列的线程池会导致内存飙升吗?

      答:会的,newFixedThreadPool使用了无界的阻塞队列LinkedBlockingQueue,如果线程获取一个任务后,任务的执行时间比较长(比如,上面demo设置了10秒),会导致队列的任务越积越多,导致机器内存使用不停飙升, 最终导致OOM。

    b.newCachedThreadPool:

    • 通俗:当有新任务到来,则插入到SynchronousQueue中,由于SynchronousQueue是同步队列,因此会在池中寻找可用线程来执行,若有可以线程则执行,若没有可用线程则创建一个线程来执行该任务;若池中线程空闲时间超过指定大小,则该线程会被销毁。
    • 适用:执行很多短期异步的小程序或者负载较轻的服务器.(用于并发执行大量短期的小任务。)
    • 特点:
      • 核心线程数为0
      • 最大线程数为Integer.MAX_VALUE
      • 阻塞队列是SynchronousQueue
      • 非核心线程空闲存活时间为60秒

    c.newSingleThreadExecutor:

    • 通俗:创建只有一个线程的线程池,且线程的存活时间是无限的;当该线程正繁忙时,对于新任务会进入阻塞队列中(无界的阻塞队列)
    • 适用:适用于串行执行任务的场景,一个任务一个任务地执行。
    • 特点:
      • 核心线程数为1
      • 最大线程数也为1
      • 阻塞队列是LinkedBlockingQueue
      • keepAliveTime为0

    d.newScheduledThreadPool:

    • 通俗:创建一个固定大小的线程池,线程池内线程存活时间无限制,线程池可以支持定时及周期性任务执行,如果所有线程均处于繁忙状态,对于新任务会进入DelayedWorkQueue队列中,这是一种按照超时时间排序的队列结构
    • 适用:周期性执行任务的场景,需要限制线程数量的场景
    • 特点:
        • 最大线程数为Integer.MAX_VALUE
        • 阻塞队列是DelayedWorkQueue
        • keepAliveTime为0
        • scheduleAtFixedRate() :按某种速率周期执行
        • scheduleWithFixedDelay():在某个延迟后执行

    2.线程池状态:

    线程池有这几个状态:RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED。

    RUNNING

    • 该状态的线程池会接收新任务,并处理阻塞队列中的任务;
    • 调用线程池的shutdown()方法,可以切换到SHUTDOWN状态;
    • 调用线程池的shutdownNow()方法,可以切换到STOP状态;

    SHUTDOWN

    • 该状态的线程池不会接收新任务,但会处理阻塞队列中的任务;
    • 队列为空,并且线程池中执行的任务也为空,进入TIDYING状态;

    STOP

    • 该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务;
    • 线程池中执行的任务为空,进入TIDYING状态;

    TIDYING

    • 该状态表明所有的任务已经运行终止,记录的任务数量为0。
    • terminated()执行完毕,进入TERMINATED状态

    TERMINATED

    • 该状态表示线程池彻底终止

    ThreadPoolExecutor的运行状态有5种,分别为:

    其生命周期转换如下入所示:

     

     3.线程池ThreadPoolExecutor中定义的生命周期中的状态及相关方法:

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3; // =29
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1; // =000 11111...
    
    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS; // 111 00000...
    private static final int SHUTDOWN   =  0 << COUNT_BITS; // 000 00000...
    private static final int STOP       =  1 << COUNT_BITS; // 001 00000...
    private static final int TIDYING    =  2 << COUNT_BITS; // 010 00000...
    private static final int TERMINATED =  3 << COUNT_BITS; // 011 00000...
    
    // 线程池的状态
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    // 线程池中工作线程的数量
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    // 计算ctl的值,等于运行状态“加上”线程数量
    private static int ctlOf(int rs, int wc) { return rs | wc; }
  • 相关阅读:
    javascript定义
    JavaScript学习笔记
    PostgreSQL数据库配置
    python 时间戳转时间 timestamp to time
    WebGIS:Vue+Flask+PostGIS+Geoserver开发环境配置
    GeoServer环境配置
    Vue前端环境配置
    Flask后端环境配置
    1.顺序表
    汇编语法--AT&T VS. intel
  • 原文地址:https://www.cnblogs.com/jingpeng77/p/12486872.html
Copyright © 2020-2023  润新知