创建线程池详解

线程池的特点,优势,创建

特点

用线程池控制运行线程的数量, 将处理中的线程任务放入队列, 线程创建后启动这些任务, 当线程超出最大数量的时候, 进入队列排队, 等其他线程执行完毕,再从队列中取出任务执行.

优势

  1. 降低资源消耗, 重复利用线程, 降低线程创建与销毁的消耗
  2. 提供响应速度, 当任务到达时, 任务不需要等待线程创建, 可以立即执行
  3. 提高线程管理, 统一分配, 调优, 监控
  4. 合理使用线程池可以防止处理OOM问题

创建

阿里编码规范.png

ThreadPoolExecutors线程池7大参数

  1. corePoolSize: 线程池中的核心线程数
  2. maximumPoolSize: 最大线程数, 必须大于1
  3. keepAliveTime: 多余的空闲时间
  4. unit: keepAliveTime单位
  5. queueCapacity: 任务队列
  6. threadFactory: 线程池中线程工厂
  7. rejectedExecutionHandler: 拒绝策略

ThreadPoolExecutors线程池4种拒绝策略

  1. AbortPolicy(默认): 直接抛异常
  2. CallerRunsPolicy: "调用者运行"一种调试机制, 将任务返回调用者
  3. DiscardOldestPolicy: 抛弃队列中等待最久的任务
  4. DiscardPolicy: 直接丢弃任务

ThreadPoolExector线程池原理分析

public class ThreadPoolExecutorTest {
    private static ThreadPoolExecutor executor =
            new ThreadPoolExecutor(
                    2,  // 核心线程数2
                    5, // 最大线程数5
                    4,
                    TimeUnit.MILLISECONDS,
                     new ArrayBlockingQueue<Runnable>(4)); // 队列数4

    public static void main(String[] args) throws InterruptedException {
        for (int i = 1; i < 15; i++) {
            final String b = String.valueOf(i);
                    TimeUnit.MILLISECONDS.sleep(100);
            executor.execute(() -> {
                System.out.println(Thread.currentThread().getName() + ": " + b);
               try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        TimeUnit.SECONDS.sleep(20);
    }
}
执行结果.png

线程池工作原理.png

线程池合理配置线程数

1)根据几个值来决定
(1) tasks: 每秒的任务数, 假设100 ~ 1000
(2) taskcost: 每个任务花费时间: 假设0.1s
(3) responsetime: 系统允许最大响应时间, 假设1s

2)根据8020定律, corePoolSize计算:
任何一组东西中,最重要的只占其中一小部分,约20%,其余80%尽管是多数,却是次要的,因此又称二八定律
corePoolSize = tasks* taskcout = (100 ~ 1000) * 0.1 = 10 ~ 100
8020定律, 核心线程数为20

3)queueCapacity计算:
任务队列的长度要根据核心线程数,以及系统对任务响应时间的要求有关。队列长度可以设置为
queueCapacity = corePoolSize / taskcost * responsetime = 20 / 0.1 * 1 = 200
注: 若将队列长度设置为Integer.MAX_VALUE,将会导致线程数量永远为corePoolSize,再也不会增加,当任务数量陡增时,任务响应时间也将随之陡增

4)maxPoolSize计算:
当系统负载达到最大值时,核心线程数已无法按时处理完所有任务,这时就需要增加线程
maxPoolSize = (max(sasks) - queueCapacity) * taskcost = (1000 -200) * 0.1 = 80

  1. rejectedExecutionHandler:根据具体情况来决定,任务不重要可丢弃,任务重要则要利用一些缓冲机制来处理

6)keepAliveTime和unit采用默认通常能满足

合理创建线程池

 ThreadPoolExecutor executor =
            new ThreadPoolExecutor(
                    20, //核心线程数
                    80,  //最大线程数
                    1,  // 多余的空闲时间
                    TimeUnit.SECONDS, //多余的空闲时间单位
                    new ArrayBlockingQueue<>(200) //队列长度 
                    ,new ThreadFactoryBuilder().setNameFormat("xx-pool-%d").build(), //创建
                    new ThreadPoolExecutor.DiscardPolicy()  //处理策略

            );
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容