线程池的好处:segmentfault
1,由于线程是比较昂贵的资源,避免大量重复建立销毁线程,使用者不用关心建立销毁线程。服务器
2,用户提交的任务可以及时的获得处理,提升响应速度。多线程
3,可以更好的监控和管理线程。函数
这些参数这样描述起来很空洞,下面结合执行任务的流程来看一下。spa
当线程池大小 >= corePoolSize 且 队列未满时,这时线程池使用者与线程池之间构成了一个生产者-消费者模型。线程池使用者生产任务,线程池消费任务,任务存储在BlockingQueue中,注意这里入队使用的是offer,当队列满的时候,直接返回false,而不会等待。操作系统
当线程处于空闲状态时,线程池须要对它们进行回收,避免浪费资源。但空闲多长时间回收呢,keepAliveTime就是用来设置这个时间的。默认状况下,最终会保留corePoolSize个线程避免回收,即便它们是空闲的,以备不时之需。但咱们也能够改变这种行为,经过设置allowCoreThreadTimeOut(true)
。.net
线程池所使用的缓冲队列,该缓冲队列的长度决定了可以缓冲的最大数量,缓冲队列有三种通用策略:线程
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; //纳秒
Executors 是提供了一组工厂方法用于建立经常使用的 ExecutorService ,分别是 FixedThreadPool,CachedThreadPool 以及 SingleThreadExecutor。这三种ThreadPoolExecutor都是调用 ThreadPoolExecutor 构造函数进行建立,区别在于参数不一样。
下面是 Executors 类 newFixedThreadPool 方法的源码:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
能够看到 corePoolSize 和 maximumPoolSize 设置成了相同的值,此时不存在线程数量大于核心线程数量的状况,因此KeepAlive时间设置不会生效。任务队列使用的是不限制大小的 LinkedBlockingQueue ,因为是无界队列因此容纳的任务数量没有上限。
所以,FixedThreadPool的行为以下:
从线程池中获取可用线程执行任务,若是没有可用线程则使用ThreadFactory建立新的线程,直到线程数达到nThreads
线程池线程数达到nThreads之后,新的任务将被放入队列
FixedThreadPool的优势是可以保证全部的任务都被执行,永远不会拒绝新的任务;同时缺点是队列数量没有限制,在任务执行时间无限延长的这种极端状况下会形成内存问题。
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相同,当处理任务无限等待的时候会形成内存问题。
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对任务的处理策略是提交的任务会当即分配一个线程进行执行,线程池中线程数量会随着任务数的变化自动扩张和缩减,在任务执行时间无限延长的极端状况下会建立过多的线程。
参考网址:http://blog.csdn.net/ghsau/article/details/53538303
https://segmentfault.com/a/1190000008394155