• 线程池的执行原则及配置参数详解


    池是一种非常优秀的设计思想,通过建立池可以有效的利用系统资源,节约系统性能。Java 中的线程池就是一种非常好的实现,从 JDK 1.5 开始 Java 提供了一个线程工厂 Executors 用来生成线程池,通过 Executors 可以方便的生成不同类型的线程池。但是要更好的理解使用线程池,就需要了解线程池的配置参数意义以及线程池的具体工作机制

    线程池的好处

    引用自 并发编程网 - ifeve.com 的说明:

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

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

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

    线程的理解:

    比如去火车站买票, 有10个售票窗口, 但只有5个窗口对外开放. 那么对外开放的5个窗口称为核心线程数, 

    而最大线程数是10个窗口.如果5个窗口都被占用, 那么后来的人就必须在后面排队, 但后来售票厅人越来越多, 已经人满为患, 就类似于线程队列已满.这时候火车站站长下令, 把剩下的5个窗口也打开, 也就是目前已经有10个窗口同时运行. 后来又来了一批人,10个窗口也处理不过来了, 而且售票厅人已经满了, 这时候站长就下令封锁入口,不允许其他人再进来, 这就是线程异常处理策略.而线程存活时间指的是, 允许售票员休息的最长时间, 以此限制售票员偷懒的行为.

    创建线程池

    //参数初始化
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    //核心线程数量大小
    private static final int corePoolSize = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    //线程池最大容纳线程数
    private static final int maximumPoolSize = CPU_COUNT * 2 + 1;
    //线程空闲后的存活时长
    private static final int keepAliveTime = 30;
    
    //任务过多后,存储任务的一个阻塞队列
    BlockingQueue<Runnable>  workQueue = new SynchronousQueue<>();
    
    //线程的创建工厂
    ThreadFactory threadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);
    
        public Thread newThread(Runnable r) {
            return new Thread(r, "AdvacnedAsyncTask #" + mCount.getAndIncrement());
        }
    };
    
    //线程池任务满载后采取的任务拒绝策略
    RejectedExecutionHandler rejectHandler = new ThreadPoolExecutor.DiscardOldestPolicy();
    
    //线程池对象,创建线程
    ThreadPoolExecutor mExecute = new ThreadPoolExecutor(
            corePoolSize, 
            maximumPoolSize,
            keepAliveTime,
            TimeUnit.SECONDS,
            workQueue,
            threadFactory, 
            rejectHandler
    );
    

    具体参数介绍

    • corePoolSize:核心线程数
      • 核心线程会一直存活,及时没有任务需要执行
      • 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理
      • 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭
    • queueCapacity:任务队列容量(阻塞队列)
      • 当核心线程数达到最大时,新任务会放在队列中排队等待执行
    • maxPoolSize:最大线程数
      • 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务
      • 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常
    • keepAliveTime:线程空闲时间
      • 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
      • 如果allowCoreThreadTimeout=true,则会直到线程数量=0
    • allowCoreThreadTimeout:允许核心线程超时
    • rejectedExecutionHandler:任务拒绝处理器
      • 两种情况会拒绝处理任务:
        • 当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务
        • 当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务
        • 线程池会调用rejectedExecutionHandler来处理这个任务。如果没有设置默认是AbortPolicy,会抛出异常
        • ThreadPoolExecutor类有几个内部实现类来处理这类情况:
          • AbortPolicy 丢弃任务,抛运行时异常
          • CallerRunsPolicy 执行任务
          • DiscardPolicy 忽视,什么都不会发生
          • DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务
        • 实现RejectedExecutionHandler接口,可自定义处理器

    ThreadPoolExecutor执行顺序:

    •      线程池按以下行为执行任务
        • 当线程数小于核心线程数时,创建线程。
        • 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
        • 当线程数大于等于核心线程数,且任务队列已满
          1. 若线程数小于最大线程数,创建线程
          2. 若线程数等于最大线程数,抛出异常,拒绝任务

    如何设置参数

    • 默认值
      • corePoolSize=1
      • queueCapacity=Integer.MAX_VALUE
      • maxPoolSize=Integer.MAX_VALUE
      • keepAliveTime=60s
      • allowCoreThreadTimeout=false
      • rejectedExecutionHandler=AbortPolicy()
    • 如何来设置
      • 需要根据几个值来决定
        • tasks :每秒的任务数,假设为500~1000
        • taskcost:每个任务花费时间,假设为0.1s
        • responsetime:系统允许容忍的最大响应时间,假设为1s
      • 做几个计算
        • corePoolSize = 每秒需要多少个线程处理? 
          • threadcount = tasks/(1/taskcost) =tasks*taskcout =  (500~1000)*0.1 = 50~100 个线程。corePoolSize设置应该大于50
          • 根据8020原则,如果80%的每秒任务数小于800,那么corePoolSize设置为80即可
        • queueCapacity = (coreSizePool/taskcost)*responsetime
          • 计算可得 queueCapacity = 80/0.1*1 = 80。意思是队列里的线程可以等待1s,超过了的需要新开线程来执行
          • 切记不能设置为Integer.MAX_VALUE,这样队列会很大,线程数只会保持在corePoolSize大小,当任务陡增时,不能新开线程来执行,响应时间会随之陡增。
        • maxPoolSize = (max(tasks)- queueCapacity)/(1/taskcost)
          • 计算可得 maxPoolSize = (1000-80)/10 = 92
          • (最大任务数-队列容量)/每个线程每秒处理能力 = 最大线程数
        • rejectedExecutionHandler:根据具体情况来决定,任务不重要可丢弃,任务重要则要利用一些缓冲机制来处理
        • keepAliveTime和allowCoreThreadTimeout采用默认通常能满足
    • 以上都是理想值,实际情况下要根据机器性能来决定。如果在未达到最大线程数的情况机器cpu load已经满了,则需要通过升级硬件(呵呵)和优化代码,降低taskcost来处理。
  • 相关阅读:
    在 Tomcat 8 部署多端口项目
    tar -zxvf jdk-8u144-linux-x64.tar.gz
    linux下删除文件夹的命令
    springboot+mybatis案例
    阿里云主机密码
    查看公钥
    jenkins安装
    redis详解(包含使用场景)
    什么是JSONP?
    在CentOS7上面搭建GitLab服务器
  • 原文地址:https://www.cnblogs.com/Pjson/p/8782930.html
Copyright © 2020-2023  润新知