线程池 -- ThreadPoolExecutor

2016-09-18 18:00:09   最后更新: 2016-09-18 18:00:09   访问数量:490




前面我们介绍了 Executor 框架

Executor 框架及线程池的使用

利用 Executor 框架,我们可以实现多个线程的并发调用

基于 Executor 框架,java 提供了 ThreadPoolExecutor 实现了一个灵活、稳定的线程池,允许用户各种定制,同时,他还可以通过构造方法实例化一个对象来让用户根据自己的需求定制化该对象的操作

 

ThreadPoolExecutor 的构造方法

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { ... }

 

 

通过上面的构造方法,我们就可以轻松创建一个线程池,他有下列参数:

  1. corePoolSize -- 线程池目标大小,即在没有任何任务时的线程池大小,只有当工作队列满了以后,线程池才会创建超过该数量的线程来扩充线程池
  2. maximumPoolSize -- 线程池的最大大小,线程池中可以同时活动的线程数量的上限
  3. keepAliveTime -- 线程存活时间,如果线程池中某个线程空闲状态的时间超过了该时间,并且线程池中线程数大于 corePoolSize,则会被标记为可回收
  4. unit -- keepAliveTime 的时间单位
  5. workQueue -- 当线程池中所有线程都处于非空闲状态,新的任务将会被缓存在该队列中,只有当队列满时,线程池才会创建新的线程扩充线程池
  6. threadFactory -- Executor 用于创建线程的线程工厂
  7. handler -- 饱和策略

 

通过 Executors 提供的工厂方法创建线程池

除了使用上面介绍的构造方法来创建线程池,我们也可以直接使用前面介绍的 Executors 所提供的工厂方法来创建线程池:

ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);

 

或:

ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) Executors.newCachedThreadPool();

 

 

  1. 相对于上面我们所介绍的线程池构造方法,Executors.newFixedThreadPool 方法相当于将基本大小和最大大小指定为相同的值,即传入参数,超时为 0,并且使用一个无界的 LinkedBlockingQueue 作为任务队列
  2. Executors.newCachedThreadPool 方法则将 corePoolSize 设置为 0,将 maximumPoolSize 设置为 Integer.MAX_VALUE,超时时间为 1 分钟,实现线程的无限扩展与自动收缩,同样,他也使用一个无界的 LinkedBlockingQueue 作为任务队列
  3. Executors.newSingleThreadExecutor 方法可以用来创建一种独特的线程池,他限制了只能有一个线程处于存活状态,这意味着绝对不可能有并发的发生

 

线程池一方面提供多个线程并发执行的环境,另一方面也限制着可并发执行的任务数量,确保不会有过多的线程同时执行使负载超过限度

如果线程池较小而任务队列较大,可以有效减少资源使用量与 CPU 负载,但是可能会限制吞吐量

通常,我们使用有界队列作为任务队列,当队列被填满以后,饱和策略就开始发挥作用了

JDK 提供了集中不同的的 RejectedExecutionHandler 实现,每种实现都包含了不同的饱和策略:

  1. AbortPolicy -- 中止策略,默认的饱和策略,他会抛出 RejectedExecutorException 以试图让线程中止,当然,调用者也可以捕获这个异常
  2. CallerRunsPolicy -- 调用者运行实现了一种调节机制,当线程池被占用,下一个任务会在主线程中调用 execute 方法执行
  3. DiscardPolicy -- 抛弃策略,意味着接下来的所有任务会被直接丢弃
  4. DiscardOldestPolicy -- 抛弃最旧策略,顾名思义,是抛弃最队列中最先取到的任务,此后的任务并不会被立即抛弃

需要注意的是,如果线程池的任务队列使用的是优先级队列,抛弃最旧策略将抛弃优先级最高的任务,这通常是我们不希望看到的

 

下面的例子创建了一个抛弃最旧策略的线程池:

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(100, 100, 0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(), new ThreadPoolExecutor.DiscardOldestPolicy());

 

 

每当线程池需要创建一个线程时,都是通过线程工厂方法来完成的

ThreadPoolExecutor 使用的是一个 ThreadFactory 接口的实现:

public interface ThreadFactory { Thread newThread(Runnable r); }

 

 

你可以通过实现自己的 ThreadFactory 定制化线程的创建,然后通过 ThreadPoolExecutor 的 setThreadFactory 方法传入你的线程工厂

 

ThreadPoolExecutor 的设计考虑到了可扩展性,你可以通过实现自己的子类来定制化生成你实际需要的线程池

ThreadPoolExecutor 提供了用于在子类中覆盖的方法:beforeExecute、afterExecute、terminated 等,这些方法会在相应事件发生后自动被调用

 






读书笔记      技术帖      线程      thread      线程池      java      threadpool     


京ICP备15018585号