ThreadPoolExecutor
在 Executoors 中的 newChachedThreadPool, newFixedThreadPool, newScheduledThreadExecutor 的工厂方法返回的都是 ThreadPoolExecutor 对象。如果默认的执行策略不能满足需求,可以通过 ThreadPoolExecutor 的构造函数进行实例化,根据自己的需求定制。
)ThreadPoolExecutor(int corePoolSize,//线程池基本大小
int maximumPoolSize, //线程池最大尺寸
long keepAliveTime, //线程空闲后的存活时间
TimeUnit unit,
BlockingQueue<Runnable> workQueue, //任务队列
ThreadFactory threadFactory, //线程工厂
RejectedExecutionHandler handler) //饱和策略
线程创建和销毁
- corePoolSize是线程池的基本大小,即在没有任务执行时线程池的大小。
- 在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务。
- 可以调用prestartAllCoreThreads()或者prestartCoreThread()方法预创建线程,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。
- 默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中
- 在工作队列满了的情况下才会创建超出这个数量的线程。
- maximumPoolSize是线程池的最大尺寸,表示可以同时活动的线程数量的上限。
- keepAliveTime 和 unit 共同表示了线程空闲后的存活时间
- 默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize。也就是当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。
- 如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0
newFixedThreadPool 工厂方法将线程池的基本大小和最大大小都设置为参数中指定的值,而且创建的线程池不会超时。
newCachedThreadPool工厂方法将线程池的最大大小设置为Integer.MAX_VALUE,将基本大小设置为0,并将超时设置为1分钟,这种方法创建出来的线程池可以被无限扩展,并且当需求降低时会自动收缩。
管理队列任务
使用BlokcingQueue来保存等待执行的任务。基本的队列有无界队列,有界队列和同步移交。队列的选择和线程池的其他配置参数有关。
newFixedThreadPool 和 newSingleThreadExecutor 在默认情况下使用一个无界的LinkedBlockingQueue。如果所有工作者线程都处于忙碌状态, 那么任务将在队列中等待。
有界队列包括ArrayBlockingQueue以及有界的LinkedBlockingQueue和PriorityBlockingQueue。在使用有界队列时,队列的大小需要和线程池的大小一起调节。线程池小而队列较大,有助于减少内存使用量,降低CPU的使用率,同时还可以减少上下文切换,但是可能会限制吞吐量。
对于非常大或无界的线程池,可以通过SynchronousQueue来避免任务排队,以及直接将任务从生产者移交给工作者线程。这个队列的put方法会阻塞,直到有线程准备从队列里面take,所以本质上SynchronousQueue并不是Queue,它不存储任何东西,它只是在移交东西,是一种在线程之间进行移交的机制。要将一个任务放到其中,必须有另一个线程正在等待接受这个元素。如果没有线程正在等待,并且线程池的当前大小小于最大值,那么ThreadPoolExecutor将创建一个新的线程,否则这个任务奖杯拒绝。在 newCachedThreadPool中采用了SynchronousQueue。
饱和策略
当有界对垒被填满后,饱和策略开始发挥作用。通过setRejectedExecutionHandler来修改。有以下四种饱和策略。
- AbortPolicy: 饱和策略,使用这种策略的线程池,将在无法继续接受新任务时,给任务提交方抛出RejectedExecutionException,让他们决定要如何处理
- DiscardPolicy:抛弃策略,直接丢弃掉新来的任务
- CallerRunsPolicy:调用者运行策略,这个策略,顾名思义,将把任务交给调用方所在的线程去执行
- Discard-OldestPolicy: 抛弃最旧的策略,抛弃下一个奖杯执行的任务,然后尝试重新提交新的任务。(如果是优先级队列,会抛弃优先级最高的任务,最好不要一起使用)
线程工厂
线程池在创建线程时,通过线程工厂方法来完成。在默认的ThreadFactory接口中只定义了一个方法newThread,每当创建新线程时都会调用这个方法。可以自己创建一个类实现默认的ThreadFactory接口来定制自己的线程工厂。
扩展ThreadPoolExecutor
ThreadPoolExecutor是可以扩展的,它提供了几个可以在子类中改写的方法:beforeExecute, afterExecute, terminated。在执行任务的线程中将调用beforeExecute和afterExecute方法。无论是正常返回还是抛出异常,afterExecute都被调用。如果beforeExecute抛出一个RuntimeException,任务将不被执行,afterExecute也不会调用。