zoukankan      html  css  js  c++  java
  • JAVA 线程池原理

    如果并发的请求数量非常多,但每个线程执行的时间很短,这样就会频繁的创建和销毁线程,如此一来会大大降低系统的效率。这就是线程池的目的了。线程池为线程生命周期的开销和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。

    线程池主要流程

    用户通过submit提交一个任务,线程池会执行如下流程:

    1. 判断当前运行的worker数量是否超过corePoolSize,如果不超过corePoolSize。就创建一个worker直接执行该任务。—— 线程池最开始是没有worker在运行的
    2. 如果正在运行的worker数量超过或者等于corePoolSize,那么就将该任务加入到workQueue队列中去。
    3. 如果workQueue队列满了,也就是offer方法返回false的话,就检查当前运行的worker数量是否小于maximumPoolSize,如果小于就创建一个worker直接执行该任务。
    4. 如果当前运行的worker数量是否大于等于maximumPoolSize,那么就执行RejectedExecutionHandler来拒绝这个任务的提交。
    5. workQueue:任务的阻塞队列,缓存将要执行的Runnable任务,由各线程轮询该任务队列获取任务执行。可以选择以下几个阻塞队列:
      1. ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
      2. LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
      3. SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
      4. PriorityBlockingQueue:一个具有优先级的无限阻塞队列。

    线程池的饱和策略

    注: 当线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:

    1. ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
    2. ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
    3. ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
    4. ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

    线程池的状态(5种):

    其中AtomicInteger变量ctl的功能非常强大:利用低29位表示线程池中线程数,通过高3位表示线程池的运行状态:

    1. RUNNING:-1 << COUNT_BITS,即高3位为111,该状态的线程池会接收新任务,并处理阻塞队列中的任务;
    2. SHUTDOWN: 0 << COUNT_BITS,即高3位为000,该状态的线程池不会接收新任务,但会处理阻塞队列中的任务;
    3. STOP : 1 << COUNT_BITS,即高3位为001,该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务;
    4. TIDYING : 2 << COUNT_BITS,即高3位为010,该状态表示线程池对线程进行整理优化;
    5. TERMINATED: 3 << COUNT_BITS,即高3位为011,该状态表示线程池停止工作;

    如何创建线程池

    Executors工厂类

    1. Executors.newCachedThreadPool();
      说明: 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.
    2. Executors.newFixedThreadPool(int);
      说明: 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    3. Executors.newSingleThreadExecutor();
      说明:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照顺序执行。
    4. Executors.newScheduledThreadPool(int);
      说明:创建一个定长线程池,支持定时及周期性任务执行。

    弊端:

    1. FixedThreadPool 和 SingleThreadPool:
      允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
    2. CachedThreadPool 和 ScheduledThreadPool:
      允许的创建线程数量为 Integer.MAX_VALUE, 可能会创建大量的线程,从而导致 OOM。

    ThreadPoolExecutor

    虽然上述Executors提供了工具类,但是依然存在可能会导致OOM的问题,我们一般建议采用SDK内部的线程池来创建,同时也可以让使用者了解每个参数的含义,防止出现问题。
    SDK内部提供了创建线程池的构造方法,如下:

    /**
    * @param corePoolSize:核心线程数;
    * @param maximumPoolSize:线程池允许的最大线程数;
    * @param keepAliveTime:保持活动时间,空闲的线程(普通线程)在超过keepAliveTime时间内没有被复用,就被销毁;
    * @param unit:时间单位;
    * @param workQueue:任务队列,用来存储已经被提交,即将被执行的任务;
    * @param threadFactory:线程工厂,用来创建线程池中的线程;
    * @param handler:拒绝策略,线程池关闭,或者最大线程数和队列已经饱和的情况下,抛出RejectedExecutionException异常;   
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
    }
  • 相关阅读:
    记录CTF一些奇技滛巧
    FISCO BCOS 多服务器分布式部署
    网络1911、1912 D&S第3次作业--批改总结
    DVWA渗透笔记
    Python——flask漏洞探究
    Java团队课程设计——基于学院的搜索引擎
    Java课程设计之——Web前端
    Java课程设计之——爬虫篇
    elasticsearch 7.5.0 学习笔记
    SpringCloud系列(一):Eureka 服务注册与服务发现
  • 原文地址:https://www.cnblogs.com/cambodia/p/12158408.html
Copyright © 2011-2022 走看看