Executor框架能够将任务的提交与任务的执行策略解耦开来。 并不是全部的任务都使用全部的执行策略,有些任务须要明确的指定执行策略,包括:安全
依赖性任务:提交给线程池的任务须要依赖其余的任务,那么就隐含地给执行策略带来了约束,此时必须当心地维持这些执行策略以免产生活跃性问题并发
使用线程封闭机制的任务:单线程的Executor可以对并发性作出更强的承诺,对象能够封闭在任务线程中,使得在该线程中执行的任务在访问该对象时不须要同步,即便这些资源不是线程安全的也没有问题。但这种情形将在任务与执行策略之间造成隐式的耦合----任务要求其执行所在的Executor是单线程的。框架
对响应时间敏感的任务函数
使用ThreadLocal的任务:只有当线程本地值的生命周期受限于任务的生命周期时,在线程池的线程中使用ThreadLocal才有意义,而在线程池的线程中不该该使用ThreadLocal在任务之间传递值。性能
只有当任务都是同类型的而且相互独立时,线程池的性能才能达到最佳。
线程池的理想大小取决于被提交任务的类型以及所部署系统的特性。在代码中一般不会固定线程池的大小,而应该经过某种配置机制来提供,或者根据Runtime.availableProcessors来动态计算。线程
ThreadPoolExecutor是一个灵活的、稳定的线程池,容许进行各类定制。 若是默认的执行策略不能知足需求,那么能够经过ThreadPoolExecutor的构造函数来实例化一个对象,并根据本身的需求来定制。日志
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { ... }
ThreadPoolExecutor是可扩展的,它提供了几个能够在子类化中改写的方法:beforeExecute、afterExecute和terminated,这些方法能够用于扩展ThreadPoolExecutor的行为。 在执行任务的线程中将调用beforeExecute和afterExecute等方法,在这些方法中还能够添加日志、计时、监视或统计信息收集的功能。 不管任务是从run中正常返回,仍是抛出一个异常而返回,afterExecute都会被调用,若是任务在完成后带有一个Error,那么就不会调用afterExecute。若是beforeExecute抛出一个RuntimeException,那么任务将不被执行,而且afterExecute也不会被调用。在线程池完成关闭操做时调用terminated,也就是在全部任务都已经完成而且全部工做者线程已经关闭后。code
线程池的基本大小(Core Pool Size)、最大大小(Maximum Pool Size)以及存活时间等因素共同负责线程的建立与销毁。对象
基本大小:线程池的目标大小,即在没有任务执行时线程池的大小,而且只有在工做队列满了的状况下才会建立超出这个数量的线程。生命周期
最大大小:可同时活动的线程数量的上限。
存活施加:若是某个线程的空闲时间超过了存活时间,那么将被标记为可回收的,而且当线程池的当前大小超过了基本大小时,这个线程被终止。
经过调节线程池的基本大小和存活时间,能够帮助线程池祸首空闲线程占有的资源,从而使得这些资源能够用于执行其余工做。
newFixedThreadPool工厂方法将线程池的基本大小和最大大小设置为参数中指定的值,并且建立的线程池不会超时。 newCachedThreadPool工厂方法将线程池的最大大小设置为Integer.MAX_VALUE,而将基本大小设置为0,并将超时设置为1分钟。这种方法建立出来的线程池能够被无限扩展,而且当需求下降时会自动收缩。
当新的任务请求的到达速率超过了线程池的处理速率,那么新到来的请求将积累起来,在线程池中,这些请求会在一个由Executor管理的Runnable队列中等待,而不会像线程那样去竞争CPU资源。 ThreadPoolExecutor容许提供一个BlockingQueue来保存等待执行的任务,基本的任务排队方法有3种:无界队列、有界队列和同步移交。 newFixedThreadPool和newSingleThreadExecutor在默认状况下将使用一个无界的LinkedBlockingQueue。若是全部的工做者线程都处于忙碌状态,那么任务将在队列中等候。若是任务持续快速地到达,而且超过了线程池处理它们的速度,那么队列将无限制地增长。 一种更稳妥的资源管理策略是使用有界队列,例如ArrayBlockingQueue、有界的LinkedBlockingQueue、PriorityBlockingQueue。
在使用有界的工做队列时,队列的大小与线程池的大小必须一块儿调节。若是线程池较小而队列较大,那么有助于减小内存使用量,下降CPU的使用率,同时还能够减小上下文切换,但可能会限制吞吐量。对于很是大的或者无界的线程池,能够经过使用SynchronousQueue来避免任务排队,以及直接将任务从生产者移交给工做者线程。SynchronousQueue不是一个真正的队列,而是一种在线程之间进行移交的机制,要将一个元素放入SynchronousQueue中,必须有另外一个线程正在等待接受这个元素,若是没有线程正在等待,而且线程池的当前大小小于最大值,那么ThreadPoolExecutor将建立一个新的线程,不然根据饱和策略,这个任务将被拒绝。
使用直接移交将更高效,由于任务会直接移交给执行它的线程,而不是被首先放在队列中,而后由工做者线程从队列中提取该任务。只有当线程池是无界的或者能够拒绝任务时,SynchronousQueue才有实际价值。 只有当任务相互独立时,为线程池或工做队列设置界限才是合理的,若是任务之间存在依赖性,那么有界的线程池或队列就可能致使线程“饥饿”死锁问题,此时应该使用无界线程池,如newCachedThreadPool。
当有界队列被填满后,饱和策略开始发挥做用。ThreadPoolExecutor的饱和策略能够经过调用setRejectedExecutionHandler来修改。JDK提供了几种不一样的饱和策略:
每当线程池须要建立一个线程时,都是经过线程工厂方法来完成的。默认的线程工厂方法将建立一个新的、非守护的线程,而且不包含特殊的配置信息,经过制定一个线程工厂方法,能够定制线程池的配置信息。
//线程工厂原型 public interface ThreadFactory { Thread newThread(Runnable r); } //自定义的线程工厂 public class MyThreadFactory implements ThreadFactory { public Thread newThread(Runnable runnable) { ... } }