码迷,mamicode.com
首页 > 编程语言 > 详细

12.深入线程池_流程和原理

时间:2017-09-10 18:57:54      阅读:188      评论:0      收藏:0      [点我收藏+]

标签:factor   lan   shutdown   需求   操作   策略   元素   线程   表示   

参考文:http://blog.csdn.net/mark_lq/article/details/50346999

一、线程池的基本类结构

  合理利用线程池能够带来三个好处。

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

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

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

 

  Executor线程池框架最大优点是把任务的提交和执行解耦。呵护短将要执行的任务封装成Task,然后提交即可。具体来说,提交一个Callable对象给ExecutorService(如最常用的线程池ThreadPoolExecutor),将得到一个Future对象,调用Future对象的get方法等待执行结果

 

  下图是线程池所涉及到的所有类的结构图,先从整体把握下

技术分享 
??????????????图1 线程池实现原理类结构图

??上面这个图是很复杂的,涉及到了线程池内部实现原理的所有类,不利于我们理解线程池如何使用。我们先从客户端的角度出发,看看客户端使用线程池所涉及到的类结构图: 
技术分享 
??????????????图2 线程池使用的基本类结构图

??从图一可知,实际的线程池类是实现ExecutorService接口的类,有ThreadPoolExecutor、ForkJoinPool和ScheduledThreadPoolExecutor。下面以常用的ThreadPoolExecutor为例讲解。

二、线程池的实现步骤

  a)线程池的创建

技术分享

 

 

1 public ThreadPoolExecutor(int corePoolSize,
2                           int maximumPoolSize,
3                           long keepAliveTime,
4                           TimeUnit unit,
5                           BlockingQueue<Runnable> workQueue,
6                           ThreadFactory threadFactory,
7                           RejectedExecutionHandler handler)

 

注:当我们创建一个线程池的时候,并不会直接就创建出相应数量的线程

而是,只有当提交一个任务到线程池时,在当前线程数小于线程池的基本线程数数线程池时,会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程

如果调用了线程池的 prestarAllCoreThreads方法,线程池会提前创建并启动所有基本线程

 

参数说明

  1.corePoolSize  (线程池的基本线程数)如:Executors.newFixedThreadPool(5),它的基本线程数就是5

 

  2.maxinumPoolSize  (线程池最大线程数)线程池允许创建的最大线程数。如果任务队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果

  注:一般默认创建线程池的的时候,maxinumPoolSize  = corePoolSize  ,即任务队列满了的话,就直接拒绝了,不会创建线程

  关于任务队列,我们后面会再详解介绍

 

  3.keepAliveTime(线程活动保持时间) 这个参数表示,线程池的工作线程在空闲状态下,存活的时间。(默认为0,即线程闲下来就将其释放)所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率

 

  4.TimeUnit (线程活动保持时间的单位),这个参数是为前面那个参数服务的,默认为 毫秒

   

  5.workQueue(任务队列) 用于保存等待执行的任务的阻塞队列,当线程池中线程执行任务执行不过来的时候,会将等待执行的任务放到这个队列中,可以选择以下几个阻塞队列

  • ArrayBlockingQueue:是一个基于数组的有界阻塞队列,此队列按FIFO原则对元素进行排序

 

  • LinkBlockingQueue:一个基于链表结构的无界阻塞队列,此队列按FIFO排序元素,吞吐量要高于ArrayBlockingQueue,静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
  JDK中默认选用LinkedBlockingQueue作为阻塞队列的原因就在于其无界性。因为线程大小固定的线程池,其线程的数量是不具备伸缩性的,当任务非常繁忙的时候,就势必会导致所有的线程都处于工作状态,如果使用一个有界的阻塞队列来进行处理,那么就非常有可能很快导致队列满的情况发生,从而导致任务无法提交而抛出RejectedExecutionException,而使用无界队列由于其良好的存储容量的伸缩性,可以很好的去缓冲任务繁忙情况下场景,即使任务非常多,也可以进行动态扩容,当任务被处理完成之后,队列中的节点也会被随之被GC回收,非常灵活。
  
  • SynchronousQueue:一个不存储元素的无界阻塞队列,每个插入操作必须要等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态。即队列中添加任务后,必须要有线程来取走这个任务。它将任务直接提交给线程而不保持它们,如果不存在可用于立即运行任务的线程,则会构造出一个新的线程
  所以Executors.newCachedThreadPool使用了这个队列,因此它是一个缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量,它的吞吐量吞吐量通常要高于LinkedBlockingQueue
 
 
  6.ThreadFactory:用于设置创建线程的工厂,通过线程工厂给每个创建出来的线程设置更有意义的名字
 
 

  7.RejectExecutionHandler(拒绝策略):当队列和线程池都满了,什么时候会出现这种情况呢?应该是要满足: (任务队列选用的是有界的队列,任务队列满已时,且当前线程数也已经达到了线程池的最大线程数maxinumPoolSize )

  那么就必须要采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出的异常。以下是JDK1.5提供的四种策略

技术分享

 

    AbortPolicy:直接抛出异常

    CallerRunsPolicy:只用调用者所在线程来运行任务

    DiscardOldestPolicy:丢弃队列里最后一个要执行任务,并执行当前任务

    DiscardPolicy:不处理,直接丢弃

  当然也可以根据应用场景来实现RejectedExecutionHandler接口自定义拒绝策略,如记录到日志或持久化不能处理的任务

  

  由此可见,创建一个线程所需的参数非常多,线程池为我们提供了类Executors的静态工厂方法用来创建不用类型的线程池,官方也建议我们使用它,它会给上面的参数给上一些默认值

  技术分享

  当然我们也可以自己创建线程池,自由地给定参数,来更好的适应不同的场景

 

 

  b)向线程池提交任务

  有两种方式提交任务(execute 和 submit)两者执行任务最后都会通过Executor的execute方法来执行,关于 execute方法,我们后面会详解,

  两者区别:1.异常处理,两者对待run方法抛出的异常处理方式不一样

        2.有无返回值,submit有返回值,而execute没有

  具体怎么用,可以参考上一篇文章,

 

  c)线程池关闭

  1.shutdown()方法

    这个方法会平滑地关闭ExecutorService,当我们调用这个方法时,ExecutorService停止接受任何新的任务且等待已经提交的任务执行完成(已经提交的任务分两类:一类是已经在执行的,另一类是没有开始执行的),当所有已经提交的任务执行完毕后将会关闭 ExecutorService

  2.awaitTermination(long timeout,TimeUnit unit)方法

    这个方法有两个参数,一个是timeout即超时时间,另一个是unit即时间单位。这个方法会使当前关闭线程池的线程 等待 timeout时长,当超过timeout时间后,则去监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false。一般情况下会和shutdown方法组合使用

  3.shutdownNow()方法:这个方法会强制关闭ExecutorService,它将取消所有运行中的任务和在工作队列中等待的任务,这个方法返回一个List列表,列表中返回的是等待在工作队列中任务

 

三、线程池的执行流程分析

  前面提到ExecutorService的submit方法 和 execute方法都会调用Executor实现类(如ThreadPoolExecutor)的execute方法,下面我们来看看任务提交到这个方法是如何执行的,从这个方法入手分析 线程池的执行流程

源码  谷歌翻译

12.深入线程池_流程和原理

标签:factor   lan   shutdown   需求   操作   策略   元素   线程   表示   

原文地址:http://www.cnblogs.com/xuzekun/p/7491958.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!