zoukankan      html  css  js  c++  java
  • 线程池的执行原则及配置参数详解

    池是一种非常优秀的设计思想,通过建立池可以有效的利用系统资源,节约系统性能。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来处理。
  • 相关阅读:
    leetcode Convert Sorted List to Binary Search Tree
    leetcode Convert Sorted Array to Binary Search Tree
    leetcode Binary Tree Level Order Traversal II
    leetcode Construct Binary Tree from Preorder and Inorder Traversal
    leetcode[105] Construct Binary Tree from Inorder and Postorder Traversal
    证明中序遍历O(n)
    leetcode Maximum Depth of Binary Tree
    限制 button 在 3 秒内不可重复点击
    HTML 和 CSS 画三角形和画多边行基本原理及实践
    在线前端 JS 或 HTML 或 CSS 编写 Demo 处 JSbin 与 jsFiddle 比较
  • 原文地址:https://www.cnblogs.com/Pjson/p/8782930.html
Copyright © 2011-2022 走看看