zoukankan      html  css  js  c++  java
  • 并发编程-线程池(一)线程池

    一、为什么要用线程池

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

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

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

    二、ThreadPoolExecutor线程池类参数详解

           

     当线程池任务处理不过来的时候(什么时候认为处理不过来后面描述),可以通过handler指定的策略进行处理,ThreadPoolExecutor提供了四种策略:

      ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常;也是默认的处理方式。
      ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。
      ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
      ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
      可以通过实现RejectedExecutionHandler接口自定义处理方式。

    三. 线程池任务执行

    线程池状态  

    线程池的5种状态:Running、ShutDown、Stop、Tidying、Terminated。

    线程池状态转化【2】

     1、RUNNING

    (1) 状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。 

    (2) 状态切换:线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0!

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

    2、 SHUTDOWN

    (1) 状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。 

    (2) 状态切换:调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。

    3、STOP

    (1) 状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。 

    (2) 状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。

    4、TIDYING

    (1) 状态说明:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进

    行相应的处理;可以通过重载terminated()函数来实现。 

    (2) 状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。 

    当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。

    5、 TERMINATED

    (1) 状态说明:线程池彻底终止,就变成TERMINATED状态。 

    (2) 状态切换:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。

    3.1. 添加执行任务
      submit() 该方法返回一个Future对象,可执行带返回值的线程;或者执行想随时可以取消的线程。Future对象的get()方法获取返回值。Future对象的cancel(true/false)取消任务,未开始或已完成返回false,参数表示是否中断执行中的线程
      execute() 没有返回值。


    3.2. 线程池任务提交过程
      一个线程提交到线程池的处理流程如下图

             

    1、如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
    2、如果此时线程池中的数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列。
    3、如果此时线程池中的数量大于等于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
    4、如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
    5、当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
    总结即:处理任务判断的优先级为 核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。

    (注意:

    1、当workQueue使用的是无界限队列时,maximumPoolSize参数就变的无意义了,比如new LinkedBlockingQueue(),或者new ArrayBlockingQueue(Integer.MAX_VALUE);
    2、使用SynchronousQueue队列时由于该队列没有容量的特性,所以不会对任务进行排队,如果线程池中没有空闲线程,会立即创建一个新线程来接收这个任务。maximumPoolSize要设置大一点。
    3、核心线程和最大线程数量相等时keepAliveTime无作用.

    3.3. 线程池关闭
    1、shutdown() 不接收新任务,会处理已添加任务
    2、shutdownNow() 不接受新任务,不处理已添加任务,中断正在处理的任务

    线程池回收:

    4. 常用队列介绍
    ArrayBlockingQueue: 这是一个由数组实现的容量固定的有界阻塞队列.
    SynchronousQueue: 没有容量,不能缓存数据;每个put必须等待一个take; offer()的时候如果没有另一个线程在poll()或者take()的话返回false。
    LinkedBlockingQueue: 这是一个由单链表实现的默认无界的阻塞队列。LinkedBlockingQueue提供了一个可选有界的构造函数,而在未指明容量时,容量默认为Integer.MAX_VALUE。

    5. Executors线程工厂类
    1、Executors.newCachedThreadPool();
    说明:

       创建的线程池核心线程0 , 最大线程是Integer.MaxValue。 线程空闲存活时间1分钟。 默认异常拒绝策略,使用SynchronousQueue队

    特点:

      每次添加任务如果没有空闲线程就会新建一个线程去执行。
      SynchronousQueue是阻塞队列,加入任务的线程会阻塞住,直到其它线程从中取走任务才会结束阻塞
      线程创建上限近乎无限

    适用场景:

      所以它适用于任务加入比较稳当且加入间隔短的场景

    实现:

      new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS,new SynchronousQueue());

    2、Executors.newFixedThreadPool(int);
    说明: 

      核心线程和最大线程数是你传入的参数。 其他参数和 Executors.newSingleThreadExecutor一样

    实现:

      new ThreadPoolExecutor(nThreads, nThreads,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue());

    3、Executors.newSingleThreadExecutor();
    说明:

      创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照顺序执行。

    特点:

      只有一个线程

      近乎可以接收无限任务的队列, 可以堆积大量任务

      适用于任务持续加入但是任务数并不多的场景

    实现:

      new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue())

    4、Executors.newScheduledThreadPool(int);
    说明:

      创建一个定长线程池,支持定时及周期性任务执行。

    特点:

      核心线程是传入的参数,最大线程是int上线, 默认存活时间是10毫秒, 任务队列使用自己实现的DelayedWorkQueue, 拒绝策略异常策略

      加入任务的时候,会把任务和定时时间构建一个RunnableScheduledFuture对象,再把这个对象放入DelayedWorkQueue队列中,

      DelayedWorkQueue是一个有序队列, 他会根据内部的RunnableScheduledFuture的运行时间排序内部对象。

      任务加入后就会启动一个线程。 这个线程会从DelayedWorkQueue中获取一个任务。

      DelayedWorkQueue内部是按照时间从前完后获取任务的。如果任务的中的时间还没有到。 获取的就是null。 获取任务结束,线程会休眠10毫秒。所以这个定时任务的执行最小间隔是10毫秒的。

    内部实现

      new ScheduledThreadPoolExecutor(corePoolSize)

    五、线程池API --- 接口定义和实现类

    ---------------------------------------------------------------------------------------------------------------------------------------

    [1] https://www.cnblogs.com/spec-dog/p/11149741.html

    [2] https://blog.csdn.net/shahuhubao/article/details/80311992 

  • 相关阅读:
    linux常用服务安装部署
    linux系统基础优化及常用命令
    linux文档和目录结构
    【BZOJ2510】弱题
    【BZOJ3495】PA2010 Riddle
    算法笔记——2-SAT
    路面修整的题解
    三角形的题解
    流星雨的题解
    计算的题解
  • 原文地址:https://www.cnblogs.com/Jomini/p/13669993.html
Copyright © 2011-2022 走看看