zoukankan      html  css  js  c++  java
  • 线程池一

    先认识以下几个快速创建线程池的工具方法:

    Executors :java.util.concurrent包下的工具类主要用于获取其他java.util.concurrent包下的实例,例如:1>创建具备一条线程的线程池:ExecutorService=Executors.newSingleThreadExecutor() 此单线程根据FIFO串行执行所有任务,如果这个唯一的线程挂了,那么会有新的线程来替代它。2>创建固定大小的线程池:ExecutorService=Executors.newFixedThreadPool(3);任务的领取同样根据FIFO,但是为并行执行任务。新任务到达创建新的线程直至线程池中的线程数量达到设定的最大值。 3>创建不定个数可缓存线程的线程池ExecutorService=Executors.newCachedThreadPool():同样根据FIFO并行执行任务,但是执行任务的个数不定,存活的线程条数最大值由JVM虚拟机决定,存活的线程的条数的最小值由执行任务空闲程度(60S不执行任务JVM就会收回空闲线程)当任务数量增加到超过存活的线程数量时,JVM又会智能的添加新的线程。 4>创建一个延期执行或者周期性执行的线程池:ScheduledExecutorService=newScheduledThreadPool(3) 此种线程池特点在于延期性和周期性执行任务,也需要指明创建线程的条数。

    打开源码查看,我们发现以上几个快速创建线程池的方法底层都是使用ThreadPoolExecutor这个类,那么我们下面主要来研究一下此类。先看构造方法,ThreadPoolExecutor这个类一共有四个构造方法,我们来看最基本的构造方法,其他构造方法均是在此基础上进行的缩减,下面细分析

    ThreadPoolExecutor(int corePoolSize,

                               int maximumPoolSize,

                               long keepAliveTime,

                               TimeUnit unit,

                               BlockingQueue<Runnable> workQueue,

                               ThreadFactory threadFactory,

                               RejectedExecutionHandler handler)

  • corePoolSiz:线程池中线程条数基本条数,在线程池中的线程条数小于corePoolSize情况下,线程池收到任务时即便线程池中有空闲线程,线程池也会自动创建新的线程直至线程池中的线程条数==corePoolSize。如果ThreadPoolExecutor调用了prestartAllCoreThreads,那么线程池会一次创建并启动所有的corePoolSize线程。
  • maximumPoolSize:线程池允许创建线程的最多条数。此参数作用范围为:已经达到基本线程条数尚未达到最大线程条数&&任务队列为有界任务队列。
  • 线程条数控制两阶段:0------->corePoolSiz------->maximumPoolSize。线程池中线程条数的创建其实分为两个阶段,第一个阶段:0------->corePoolSiz 此时线程池中线程是否增加是由是否收到新任务决定,与是否有空闲线程无关。第二个阶段:corePoolSiz------->maximumPoolSize此时线程池中的线程已经达到基本的线程条数,此时线程池中的线程条数由有界任务队列是否已满有关,与任务个数无关。注意:此时的“任务队列是否已满”是指的有界任务队列,如果选用无界任务队列,那么maximumPoolSize参数无效。
  • keepAliveTime:线程池中的线程在执行完毕任务后,保持存活的时间。
  • TimeUnit:unit单位的意思,keepAliveTime的时间单位,线程活动保持的时间单位。
  • workQueue 任务队列,任务队列分为多种情况,不存储元素队列------>存储有限元素个数的队列(有界队列)------->存储无线元素个数的队列(无界元素队列),但是无论哪种任务队列说白了就是一集合。下面细细说来,

                      1>ArrayBlockingQueue:数组阻塞队列,它是一个基于数组结构的有界阻塞队列。按FIFO原则对任务元素进行排序。

                      2> LinkedBlockingQueue: 链表阻塞队列,它是一个基于链表结构的有界阻塞队列。此队列按FIFO原则对元素进行排序。吞吐量,通常要高于ArrayBlockingQueue,在上面提到的快速创建线程池第二种静态工厂法Executors.newFixedThreadPool(3)内部实现即为用的此队列。

                      3>SynchronousQueue:同步队列,它是一个不存储元素的阻塞队列。任务的插入操作后,必须等待线程池中的线程将此任务移除后才可以在此执行添加新任务的操作,在此期间插入操作一直处于阻塞状态。吞吐量,通常要高于LinkedBlockingQueue,在上面提到的快速创建线程池的第三种静态方法Executors.newCachedThreadPool()内部实现即为用的此队列。

                      4>PriorityBlockingQueue 优先次序无界阻塞队列,它可以任意向其中添加任务,但是它的排序并非根据FIFO原则对元素进行排序,而是根据元素的优先级进行的排序。

  • 说明:以上说的四种任务队列ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、PriorityBlockingQueue 均为BlockingQueue的子类,所以在构造方法中根据个人情况可以选用任意一种任务队列。其实BlockingQueue任务队列不止以上四种子类还有下面两个子类:DelayQueue、LinkedBlockingDeque(since1.6)。这两个子类以后再做介绍。线程池这里关于任务队列的就先介绍这些。

  • threadFactory:用于创建线程的工厂,此处可以使用工具类Executors提供的java自带的线程工厂类 Executors.defaultThreadFactory()。但是如果使用我们自身指定的线程工厂可以更好给线程起别名,这样便于debug调试。

  • handler:通过上面corePoolSiz与maximumPoolSize两个参数我们得知线程条数的创建分为两个阶段,当这两个阶段都走完后线程数量达到最大饱和状态,如果此时还在不停的添加任务致使任务队列也达到最大饱和状态,那么此时就形成双饱和状态。那么这个时候如果有新的任务提交,没有存储它的队列,更没有处理它的线程。此时就需要实施饱策略RejectedExecutionHandler接口来处理这些想要添加但是添加不进去的任务,如果执行程序已关闭,则会丢弃该任务。下面细说实现此接口的4个子类:1>ThreadPoolExecutor.AbortPolicy()无法处理新添加直接抛出执行被拒异常RejectedExecutionException (就是这么直接,就是这么生硬,哥不干了)。2>ThreadPoolExecutor.CallerRunsPolicy()不在线程池分配的线程中执行任务,而是在threadsPool.execute.execute()方法所在线程去执行,即父线程。3>ThreadPoolExecutor.DiscardOldestPolicy()丢弃任务队列中的第一个将新任务添加进去,然后重新执行threadsPool.execute.execute()方法。4>不抛出异常,直接将想要添加的任务丢弃(就是这么直接,就是这么生硬,哥不收了)。 5>说明:以上四种处理方案:1、抛异常2>父线程执行3>丢弃任务队列中第一个再添加新任务4>直接丢弃新任务 均为官方给出的解决方案,我们可以自实现java.util.concurrent.RejectedExecutionException这个接口来处理双饱和状态。

  • 其实整个构造函数基本上就是在说明两个问题:1>线程池中线程的个数控制、2>任务队列中的任务的控制。当任务队列使用无界任务队列时,那么线程最大条数maximumPoolSize这个参数就会失效,因为maximumPoolSize这个参数依赖于有界任务队列。我们可以这样记忆corePoolSize、keepAliveTime决定线程池中线程条数多少。keepAliveTime TimeUnit 决定线程生命周期。workQueue任务队列。threadFactory用于创建线程,自定义可以对线程起别名便于调试。RejectedExecutionHandler 用于处理双饱和状态。

    //-------------------------------------------------------------------------------------------------华丽分割线--------------------------------------------------------------------------------------

    上面提到了ThreadPoolExecutor线程池的创建。下面来看如何向线程池中提交任务。

    1>使用threadPoolExecutor.execute(Runnable runnable)提交任务,在Runnable中封装要执行的任务。不推荐使用这种方法,因为这种方法没有返回值。无法判断任务是否被线程池执行成功。

    2>使用threadPoolExecutor的父类AbstractExecutorService的submit提交方法

    ---未完待续

查看全文
  • 相关阅读:
    解决Java版CKFinder无法显示缩略图问题
    python视频教程大全
    关于Linux vi命令 vi命令一览表
    Python快速教程
    Linux的概念与体系
    每天一个linux命令目录
    每天一个linux命令(31): /etc/group文件详解
    每天一个linux命令(30): chown命令
    每天一个linux命令(29):chgrp命令
    十大Material Design开源项目
  • 原文地址:https://www.cnblogs.com/android001/p/4311553.html
  • Copyright © 2011-2022 走看看