对线程池简单理解

线程池的好处:segmentfault

1,由于线程是比较昂贵的资源,避免大量重复建立销毁线程,使用者不用关心建立销毁线程。服务器

2,用户提交的任务可以及时的获得处理,提升响应速度。多线程

3,可以更好的监控和管理线程。函数

ThreadPoolExecutor参数

 

    • int corePoolSize 
      • 线程池基本大小
    • int maximumPoolSize 
      • 线程池最大大小
    • long keepAliveTime 
      • 保持活动时间
    • TimeUnit unit 
      • 保持活动时间单位
    • BlockingQueue workQueue 
      • 工做队列
    • ThreadFactory threadFactory 
      • 线程工厂
    • RejectedExecutionHandler handler 
      • 驳回回调

这些参数这样描述起来很空洞,下面结合执行任务的流程来看一下。spa

ThreadPoolExecutor执行任务流程

当线程池大小 >= corePoolSize 且 队列未满时,这时线程池使用者与线程池之间构成了一个生产者-消费者模型。线程池使用者生产任务,线程池消费任务,任务存储在BlockingQueue中,注意这里入队使用的是offer,当队列满的时候,直接返回false,而不会等待。操作系统

keepAliveTime

当线程处于空闲状态时,线程池须要对它们进行回收,避免浪费资源。但空闲多长时间回收呢,keepAliveTime就是用来设置这个时间的。默认状况下,最终会保留corePoolSize个线程避免回收,即便它们是空闲的,以备不时之需。但咱们也能够改变这种行为,经过设置allowCoreThreadTimeOut(true).net

workQueue

线程池所使用的缓冲队列,该缓冲队列的长度决定了可以缓冲的最大数量,缓冲队列有三种通用策略:线程

1) 直接提交。工做队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,若是不存在可用于当即运行任务的线程,则试图把任务加入队列将失败,所以会构造一个新的线程。此策略能够避免在处理可能具备内部依赖性的请求集时出现锁。直接提交一般要求无界 maximumPoolSizes 以免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略容许无界线程具备增加的可能性;code

2) 无界队列。使用无界队列(例如,不具备预约义容量的 LinkedBlockingQueue)将致使在全部 corePoolSize 线程都忙时新任务在队列中等待。这样,建立的线程就不会超过 corePoolSize。(所以,maximumPoolSize 的值也就无效了。)当每一个任务彻底独立于其余任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略容许无界线程具备增加的可能性;blog

3) 有界队列。当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,可是可能较难调整和控制。队列大小和最大池大小可能须要相互折衷:使用大型队列和小型池能够最大限度地下降 CPU 使用率、操做系统资源和上下文切换开销,可是可能致使人工下降吞吐量。若是任务频繁阻塞(例如,若是它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列一般要求较大的池大小,CPU 使用率较高,可是可能遇到不可接受的调度开销,这样也会下降吞吐量。

unit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:

TimeUnit.DAYS;               //天  
TimeUnit.HOURS;             //小时  
TimeUnit.MINUTES;           //分钟  
TimeUnit.SECONDS;           //秒  
TimeUnit.MILLISECONDS;      //毫秒  
TimeUnit.MICROSECONDS;      //微妙  
TimeUnit.NANOSECONDS;       //纳秒 

三种经常使用的 ThreadPoolExecutor

Executors 是提供了一组工厂方法用于建立经常使用的 ExecutorService ,分别是 FixedThreadPool,CachedThreadPool 以及 SingleThreadExecutor。这三种ThreadPoolExecutor都是调用 ThreadPoolExecutor 构造函数进行建立,区别在于参数不一样。

FixedThreadPool - 线程池大小固定,任务队列无界

下面是 Executors 类 newFixedThreadPool 方法的源码:

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

能够看到 corePoolSize 和 maximumPoolSize 设置成了相同的值,此时不存在线程数量大于核心线程数量的状况,因此KeepAlive时间设置不会生效。任务队列使用的是不限制大小的 LinkedBlockingQueue ,因为是无界队列因此容纳的任务数量没有上限。

所以,FixedThreadPool的行为以下:

  1. 从线程池中获取可用线程执行任务,若是没有可用线程则使用ThreadFactory建立新的线程,直到线程数达到nThreads

  2. 线程池线程数达到nThreads之后,新的任务将被放入队列

FixedThreadPool的优势是可以保证全部的任务都被执行,永远不会拒绝新的任务;同时缺点是队列数量没有限制,在任务执行时间无限延长的这种极端状况下会形成内存问题。

SingleThreadExecutor - 线程池大小固定为1,任务队列无界

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

这个工厂方法中使用无界LinkedBlockingQueue,并的将线程数设置成1,除此之外还使用FinalizableDelegatedExecutorService类进行了包装。这个包装类的主要目的是为了屏蔽ThreadPoolExecutor中动态修改线程数量的功能,仅保留ExecutorService中提供的方法。虽然是单线程处理,一旦线程由于处理异常等缘由终止的时候,ThreadPoolExecutor会自动建立一个新的线程继续进行工做。

 

SingleThreadExecutor 适用于在逻辑上须要单线程处理任务的场景,同时无界的LinkedBlockingQueue保证新任务都可以放入队列,不会被拒绝;缺点和FixedThreadPool相同,当处理任务无限等待的时候会形成内存问题。

CachedThreadPool - 线程池无限大(MAX INT),等待队列长度为1

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

SynchronousQueue是一个只有1个元素的队列,入队的任务须要一直等待直到队列中的元素被移出。核心线程数是0,意味着全部任务会先入队列;最大线程数是Integer.MAX_VALUE,能够认为线程数量是没有限制的。KeepAlive时间被设置成60秒,意味着在没有任务的时候线程等待60秒之后退出。CachedThreadPool对任务的处理策略是提交的任务会当即分配一个线程进行执行,线程池中线程数量会随着任务数的变化自动扩张和缩减,在任务执行时间无限延长的极端状况下会建立过多的线程。

 
  1. 为何newFixedThreadPool中要将corePoolSize和maximumPoolSize设置成同样? 答:由于newFixedThreadPool中用的是LinkedBlockingQueue(是无界队列),只要当前线程大于等于corePoolSize来的任务就直接加入到无界队列中,因此线程数不会超过corePoolSize,这样maximumPoolSize没有用。例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略容许无界线程具备增加的可能性。
  2. 为何newFixedThreadPool中队列使用LinkedBlockingQueue?答:设置的corePoolSize 和 maximumPoolSize相同,则建立的线程池是大小固定的,要保证线程池大小固定则须要LinkedBlockingQueue(无界队列)来保证来的任务可以放到任务队列中,不至于触发拒绝策略。
  3. 为何newFixedThreadPool中keepAliveTime会设置成0?由于corePoolSize和maximumPoolSize同样大,KeepAliveTime设置的时间会失效,因此设置为0。
  4. 为何newCachedThreadPool中要将corePoolSize设置成0?答:由于队列使用SynchronousQueue,队列中只能存放一个任务,保证全部任务会先入队列,用于那些互相依赖的线程,好比线程A必须在线程B以前先执行。
  5. 为何newCachedThreadPool中队列使用SynchronousQueue?答:线程数会随着任务数量变化自动扩张和缩减,能够灵活回收空闲线程,用SynchronousQueue队列整好保证了CachedTheadPool的特色。
  6. 为何newSingleThreadExecutor中使用DelegatedExecutorService去包装ThreadPoolExecutor?答:SingleThreadExecutor是单线程化线程池,用DelegatedExecutorService包装为了屏蔽ThreadPoolExecutor动态修改线程数量的功能,仅保留Executor中的方法。

 参考网址:http://blog.csdn.net/ghsau/article/details/53538303

https://segmentfault.com/a/1190000008394155

相关文章
相关标签/搜索