zoukankan      html  css  js  c++  java
  • 详解线程池

    一,作用

    1.减少资源的开销,没有每次需要的提供系统资源进行创建和销毁线程,提高系统的响应

    2.请求到来,线程在池中直接使用,提高响应

    3.实现对线程的管控.线程池可以对线程的创建与停止、线程数量等等因素加以控制,使得线程在一种可控的范围内运行,不仅能保证系统稳定运行,而且方便性能调优。

    4.总结: 线程复用,线程管控,控制最大并发数(平衡高性能和高可用,稳定性)

    二,原理

    1.角色: 分成多个工作线程和一个阻塞队列,共同协作,调度并行处理任务,提任务的处理量,缩减处理时间.

    2.工作线程: 一组已经处于运行中的线程,不断向阻塞队列中领取任务执行

    3.阻塞队列: 是用来存储工作线程来不及处理的任务,当工作线程都在执行任务,新来的任务只能暂存在的阻塞队列中.

    三,参数:

    1.创建一个线程池: new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, timeUnit, runnableTaskQueue,threadFactory,handler);

    核心线程数:corePoolSize --- 空闲时间,基本保持稳定数量的线程数量

    最大线程数:maximumPoolSize  ---  最大线程数量,线程数量的上线,如果是实际线程已经达到这个上限,队列未满,任务加入队列,队列已满,执行拒绝策略.

    线程空闲存活时间:keepAliveTime --当实际线程数量超过corePoolSize时,若线程空闲的时间超过该值,就会被停止。
    PS:当任务很多,且任务执行时间很短的情况下,可以将该值调大,提高线程利用率。

    时间单位:timeUnit

    阻塞队列:runnableTaskQueue

      1. ArrayBlockingQueue
        它是一个由数组实现的阻塞队列,FIFO。
      2. LinkedBlockingQueue
        它是一个由链表实现的阻塞队列,FIFO。 吞吐量通常要高于ArrayBlockingQueue。
        fixedThreadPool使用的阻塞队列就是它。 它是一个无界队列。
      3. SynchronousQueue
        它是一个没有存储空间的阻塞队列,任务提交给它之后必须要交给一条工作线程处理;如果当前没有空闲的工作线程,则立即创建一条新的工作线程。 cachedThreadPool用的阻塞队列就是它。 它是一个无界队列。
      4. PriorityBlockingQueue
      5. 它是一个优先权阻塞队列。

    线程工厂:threadFactory

    拒绝策略:handler

      1. 当实际线程数达到maximumPoolSize,并且阻塞队列已满时,就会调用饱和策略。JDK1.5由四种饱和策略:
        1:AbortPolicy 默认。直接抛异常。
        2:CallerRunsPolicy
        只用调用者所在的线程执行任务。
        3:DiscardOldestPolicy
        丢弃任务队列中最久的任务。
        4:DiscardPolicy
        丢弃当前任务。

    ThreadPoolExecutor运行机制
    当有请求到来时:

      1. 若当前实际线程数量 少于 corePoolSize,即使有空闲线程,也会创建一个新的工作线程;
      2. 若当前实际线程数量处于corePoolSize和maximumPoolSize之间,并且阻塞队列没满,则任务将被放入阻塞队列中等待执行;
      3. 若当前实际线程数量 小于 maximumPoolSize,但阻塞队列已满,则直接创建新线程处理任务;
      4. 若当前实际线程数量已经达到maximumPoolSize,并且阻塞队列已满,则使用饱和策略。

    提交任务
    可以向ThreadPoolExecutor提交两种任务:Callable和Runnable。

    1. Callable
      该类任务有返回结果,可以抛出异常。
      通过submit函数提交,返回Future对象。
      可通过get获取执行结果。
    2. Runnable
      该类任务只执行,无法获取返回结果,并在执行过程中无法抛异常。
      通过execute提交。

    关闭线程池
    关闭线程池有两种方式:shutdown和shutdownNow,关闭时,会遍历所有的线程,调用它们的interrupt函数中断线程。但这两种方式对于正在执行的线程处理方式不同。

      1. shutdown()
        仅停止阻塞队列中等待的线程,那些正在执行的线程就会让他们执行结束。
      2. shutdownNow()
        不仅会停止阻塞队列中的线程,而且会停止正在执行的线程。

     设置合理的线程池大小
    任务一般可分为:CPU密集型、IO密集型、混合型,对于不同类型的任务需要分配不同大小的线程池。

         CPU密集型任务,尽量使用较小的线程池,一般为CPU核心数+1。

      • 因为CPU密集型任务使得CPU使用率很高,若开过多的线程数,只能增加上下文切换的次数,因此会带来额外的开销。
      • IO密集型任务
        可以使用稍大的线程池,一般为2*CPU核心数。
        IO密集型任务CPU使用率并不高,因此可以让CPU在等待IO的时候去处理别的任务,充分利用CPU时间。
      • 混合型任务
        可以将任务分成IO密集型和CPU密集型任务,然后分别用不同的线程池去处理。
        只要分完之后两个任务的执行时间相差不大,那么就会比串行执行来的高效。
        因为如果划分之后两个任务执行时间相差甚远,那么先执行完的任务就要等后执行完的任务,最终的时间仍然取决于后执行完的任务,而且还要加上任务拆分与合并的开销,得不偿失。

    Executor两级调度模型
    在这里插入图片描述

    在HotSpot虚拟机中,Java中的线程将会被一一映射为操作系统的线程。
    在Java虚拟机层面,用户将多个任务提交给Executor框架,Executor负责分配线程执行它们;
    在操作系统层面,操作系统再将这些线程分配给处理器执行。

    Executor结构在这里插入图片描述

    Executor框架中的所有类可以分成三类:

    1. 任务
      任务有两种类型:Runnable和Callable。
    2. 任务执行器
      Executor框架最核心的接口是Executor,它表示任务的执行器。
      Executor的子接口为ExecutorService。
      ExecutorService有两大实现类:ThreadPoolExecutor和ScheduledThreadPoolExecutor。
    3. 执行结果
      Future接口表示异步的执行结果,它的实现类为FutureTask。

    线程池
    Executors工厂类可以创建四种类型的线程池,通过Executors.newXXX即可创建。

    1. FixedThreadPool

    public static ExecutorService newFixedThreadPool(int nThreads){
        return new ThreadPoolExecutor(nThreads,nThreads,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
    }
    
    • 1
    • 2
    • 3

    v2-984ba405f67249ab3cc043c75dbcbedd_hd.jpg

    • 它是一种固定大小的线程池;
    • corePoolSize和maximunPoolSize都为用户设定的线程数量nThreads;
    • keepAliveTime为0,意味着一旦有多余的空闲线程,就会被立即停止掉;但这里keepAliveTime无效;
    • 阻塞队列采用了LinkedBlockingQueue,它是一个无界队列;
    • 由于阻塞队列是一个无界队列,因此永远不可能拒绝任务;
    • 由于采用了无界队列,实际线程数量将永远维持在nThreads,因此maximumPoolSize和keepAliveTime将无效。

    2. CachedThreadPool

    	public static ExecutorService newCachedThreadPool(){
    	    return new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.MILLISECONDS,new SynchronousQueue<Runnable>());
    	}
    
    • 1
    • 2
    • 3

    v2-f9cff0865c6143ace452274046322335_hd.jpg

    • 它是一个可以无限扩大的线程池;
    • 它比较适合处理执行时间比较小的任务;
    • corePoolSize为0,maximumPoolSize为无限大,意味着线程数量可以无限大;
    • keepAliveTime为60S,意味着线程空闲时间超过60S就会被杀死;
    • 采用SynchronousQueue装等待的任务,这个阻塞队列没有存储空间,这意味着只要有请求到来,就必须要找到一条工作线程处理他,如果当前没有空闲的线程,那么就会再创建一条新的线程。

    3. SingleThreadExecutor

    public static ExecutorService newSingleThreadExecutor(){
        return new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
    }
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    • 它只会创建一条工作线程处理任务;
    • 采用的阻塞队列为LinkedBlockingQueue;

    4. ScheduledThreadPool

    它用来处理延时任务或定时任务。

    • 它接收SchduledFutureTask类型的任务,有两种提交任务的方式:

    • scheduledAtFixedRate

    • scheduledWithFixedDelay

    • SchduledFutureTask接收的参数:

    • time:任务开始的时间

    • sequenceNumber:任务的序号

    • period:任务执行的时间间隔

    • 它采用DelayQueue存储等待的任务

    • DelayQueue内部封装了一个PriorityQueue,它会根据time的先后时间排序,若time相同则根据sequenceNumber排序;

    • DelayQueue也是一个无界队列;

    • 工作线程的执行过程:

    • 工作线程会从DelayQueue取已经到期的任务去执行;

    • 执行结束后重新设置任务的到期时间,再次放回DelayQueue

     

      * 线程池三种使用方式
     * 1、Executors.newFixedThreadPool(5) 适合长期任务,性能好
     * 2、Executors.newSingleThreadExecutor() 一个任务一个任务执行,保证顺序性
     * 3、Executors.newCachedThreadPool() 适合短期异步任务或者负载很轻的服务

    因为相信,所以看见.
  • 相关阅读:
    SPRINGMVC整合SOLR
    solr 嵌套entity 高亮查询
    solr 高级进阶,解决问题
    Solr的主从模式Master-Slave
    Solr8.0速成系列 | Solr客户端常用操作和查询语法 08
    solr 的全量更新与增量更新
    solr8.0.0和tomcat8.5.40的整合,完整版
    设置 Tomcat 的JVM运行内存
    mongo主库地址变更,从库修改数据源IP
    mysql数据表如何导入MSSQL中
  • 原文地址:https://www.cnblogs.com/zeenzhou/p/14624736.html
Copyright © 2011-2022 走看看