ThreadPoolExecutor

一.ThreadPoolExecutor

通过Executors,可以创建3种类型的ThreadPoolExecutor。 
- FixedThreadPool 
- SingleThreadExecutor 
- CachedThreadPool

1.FixedThreadPool 
FixedThreadPool被称为可重用固定线程数的线程池。下面是FixedThreadPool的源代码实现。

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads, 0L, 
                TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
  • 1
  • 2
  • 3
  • 4

FixedThreadPool中多余的空闲线程会被立即终止。 
FixedThreadPool的execute()运行示意图如下所示。 

  1. 如果当前运行的线程数小于corePoolSize,则创建新线程来执行任务。
  2. 当前运行的线程数等于corePoolSize,将任务加入LinkedBlockingQueue。
  3. 线程执行完1中的任务后,会反复从阻塞队列中取任务执行。

FixedThreadPool使用无界队列LinkedBlockingQueue(容量为Integer.MAX_VALUE),使用无界队列会对线程池带来以下影响。 
1. 当线程池内的线程数达到corePoolSize后,新任务将在阻塞队列中等待,因此线程池内的线程数不会超过corePoolSize; 
2. 由于1,使用无界队列时maximumPoolSize将是一个无效参数; 
3. 由于1和2,使用无界队列时keepAliveTime将是一个无效参数; 
4. 由于使用无界队列,运行中的FixedThreadPool(未执行shutdown()或shutdownNow())不会拒绝任务(不会调用RejectedExecutionHandler.rejectedExecution)。

2. SingleThreadExecutor

SingleThreadExecutor是使用单个worker线程的Executor。

 public static ExecutorService newSingleThreadExecutor() {
    return new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, 
                                new LinkedBlockingQueue<Runnable>());
  • 1
  • 2
  • 3
  • 4

3.CachedThreadPool

CachedThreadPool是一个会根据需要创建新线程的线程池。

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Interger.MAX_VALUE, 60L, TimeUnit.SECONDS, 
                    new SynchronousQueue<Runnable>());
  • 1
  • 2
  • 3
  • 4

CachedThreadPool的corePool为空,maximumPool无界,空闲线程等待新任务的最长时间是60s。CachedThreadPool使用没有容量的SysnchronousQueue作为线程池的工作队列,但maximumPool无界,意味着,如果主线程提交任务的速度高于maximumPool中线程处理任务的速度时,cachedThreadPool会不断创建新的线程。极端情况下,会因为创建线程过多而耗尽CPU和内存资源。 
CachedThreadPool的execute()方法执行示意图如下所示。

如上图所示。 
1. 首先执行SynchronousQueue.offer(Runnable task)。如果当前maximumPool中有空闲线程正在执行SynchronousQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS),那么主线程执行offer操作与空闲线程执行的poll操作配对成功,主线程把任务交给空闲线程执行。否则执行下面的步骤2. 
2. 当初始maximumPool为空,或者maximumPool中没有空闲的线程时,将没有线程执行SynchronousQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS),这种情况下步骤1将失败。此时CachedThreadPoll会创建一个新线程执行任务。 
3. 在步骤2中新创建的线程将任务执行完之后,会执行SynchronousQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)。这个poll操作会让空闲线程最多在SynchronousQueue中等待60S。

二. ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor主要用来在给定的延迟之后运行任务,或者定期执行任务。其执行示意图如下图所示。 

DelayQueue是一个无界队列,所以ThreadPoolExecutor的maximumPoolSize在ScheduledThreadPoolExecutor中没什么意义。 
ScheduledThreadPoolExecutor的执行主要分为两大部分。 
- 当调用ScheduledThreadPoolExecutor的scheduleAtFixedRate()方法或者scheduleWithFixedDelay()方法时,会向ScheduledThreadPoolExecutor的DelayQueue添加一个实现了RunnableScheduledFuture接口的ScheduledFutureTask
- 线程池中的线程从DelayQueue中获取ScheduledFutureTask, 然后执行任务。

ScheduledFutureTask主要包含3个成员变量,如下。 
- long型成员变量time,表示这个任务将要被执行的具体时间 
- long型成员变量sequenceNumber,表示这个任务被添加到ScheduledThreadPoolExecutor中的序号。 
- long型成员变量period,表示任务执行的时间间隔。

DelayQueue封装了一个PriorityQueue, 这个PriorityQueue会对队列中的ScheduledFutureTask进行排序。排序时, time小的排在前面,若time相同,则sequenceNumber小的排在前面。

ScheduledThreadPoolExecutor中的线程A执行某个周期任务分为4个步骤; 
1. 线程A从DelayQueue中获取已经到期的ScheduledFutureTask(DelayQueue.take()); 
2. 执行这个任务; 
3. 修改这个任务的time变量为下次将要执行的时间; 
4. 把该任务放回DelayQueue中(DelayQueue.add())。

参考《Java并发编程的艺术》

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值