Tomcat的StandardThreadExecutor

本文深入分析了Tomcat中的StandardThreadExecutor线程池执行策略,包括其如何优先扩充线程到最大数量再尝试将任务添加到队列,以及在队列满时的重试机制。

  JDK中的ThreadExecutor中的execute方法的处理逻辑应该都知道:

1.小于等于Coresize:创建线程执行;
2.大于CoreSize:加入队列;
3.队列满且小于maxSize:有空闲线程使用空闲线程执行,没有的话,创建线程执行;如果大于maxSize则拒绝策略执行。

  这样会造成一个现象,如果设置的不恰当,队列使用LinkedBlockingQueue,那么线程数量是很不可能达到maximumPoolSize,因为线程数量到了corePoolSize之后,之后新的任务是添加到队列里面去了。
  Tomcat中有一个StandardThreadExecutor线程池,该线程池execute执行策略是优先扩充线程到maximumPoolSize,再offer到queue,如果满了就reject。来看看StandardThreadExecutor是怎么实现的。
  首先Tomcat的StandardThreadExecutor类实现了org.apache.catalina.Executor接口,该接口继承了java.util.concurrent.Executor。StandardThreadExecutor内部组合了一个org.apache.tomcat.util.threads.ThreadPoolExecutor,该类继承了java.util.concurrent.ThreadPoolExecutor
  先来关注一下java.util.concurrent.ThreadPoolExecutor的execute方法:

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    else if (!addWorker(command, false))
        reject(command);
}

  addWorker是创建线程执行任务,该方法中会判断当前的线程数量是否达到maximumPoolSize;workQueue.offer是将任务添加到队列里面。
  org.apache.tomcat.util.threads.ThreadPoolExecutor就是对workQueue作了一些改动。StandardThreadExecutor使用的是org.apache.tomcat.util.threads.TaskQueue

public class TaskQueue extends LinkedBlockingQueue<Runnable> {

    private volatile ThreadPoolExecutor parent = null;

    public boolean force(Runnable o) {
        if ( parent==null || parent.isShutdown() ) throw new RejectedExecutionException("Executor not running, can't force a command into the queue");
        return super.offer(o); //forces the item onto the queue, to be used if the task is rejected
    }

    public boolean force(Runnable o, long timeout, TimeUnit unit) throws InterruptedException {
        if ( parent==null || parent.isShutdown() ) throw new RejectedExecutionException("Executor not running, can't force a command into the queue");
        return super.offer(o,timeout,unit); //forces the item onto the queue, to be used if the task is rejected
    }

    @Override
    public boolean offer(Runnable o) {
      //we can't do any checks
        if (parent==null) return super.offer(o);
        //we are maxed out on threads, simply queue the object
        if (parent.getPoolSize() == parent.getMaximumPoolSize()) return super.offer(o);
        //we have idle threads, just add it to the queue
        if (parent.getSubmittedCount()<(parent.getPoolSize())) return super.offer(o);
        //if we have less threads than maximum force creation of a new thread
        if (parent.getPoolSize()<parent.getMaximumPoolSize()) return false;
        //if we reached here, we need to add it to the queue
        return super.offer(o);
    }

    //……
}

  看一下TaskQueue的offer方法,是不是明白了?当parent.getPoolSize()还没达到parent.getMaximumPoolSize()大小时,返回false,这样会走addWorker分支。再来看org.apache.tomcat.util.threads.ThreadPoolExecutor的execute:

public void execute(Runnable command, long timeout, TimeUnit unit) {
    submittedCount.incrementAndGet();
    try {
        super.execute(command);
    } catch (RejectedExecutionException rx) {
        if (super.getQueue() instanceof TaskQueue) {
            final TaskQueue queue = (TaskQueue)super.getQueue();
            try {
                if (!queue.force(command, timeout, unit)) {
                    submittedCount.decrementAndGet();
                    throw new RejectedExecutionException("Queue capacity is full.");
                }
            } catch (InterruptedException x) {
                submittedCount.decrementAndGet();
                throw new RejectedExecutionException(x);
            }
        } else {
            submittedCount.decrementAndGet();
            throw rx;
        }
    }
}

  当抛出RejectedExecutionException时,会等待timeout后重试一次加到queue中,如果失败才最终抛出异常。
  补充一下,StandardThreadExecutor指定的RejectedExecutionHandler是RejectHandler:

    private static class RejectHandler implements RejectedExecutionHandler {
        @Override
        public void rejectedExecution(Runnable r,
                java.util.concurrent.ThreadPoolExecutor executor) {
            throw new RejectedExecutionException();
        }
    }

  OK,StandardThreadExecutor介绍完了,那么留个思考题,StandardThreadExecutor在使用时有啥局限性?

StandardThreadExecutorTomcat中的一个线程池实现类,它是基于Java标准库中的线程池实现的。其实现原理如下: 1. 初始化线程池:在StandardThreadExecutor的构造函数中,会初始化一个ThreadPoolExecutor对象,用于管理线程池中的线程。ThreadPoolExecutor是Java标准库中的一个线程池实现类,它提供了一些方法,例如execute()、shutdown()等,用于管理线程池中的线程。 2. 处理请求:当有请求到达Tomcat服务器时,Tomcat会将请求交给线程池中的线程来处理。线程池会从线程池中选取一个空闲线程来处理请求,如果线程池中没有空闲线程,则会创建一个新的线程来处理请求。 3. 线程池调度:线程池会根据预设的参数来调度线程。例如,如果线程池中的线程数超过了预设的最大线程数,则不会再创建新的线程;如果线程池中的线程数少于预设的最小线程数,则会创建新的线程。 4. 线程池监控:线程池中的线程会定期向线程池汇报自己的状态,例如空闲时间、执行任务数等。线程池可以根据这些信息来调整线程池的大小,以充分利用系统资源。 总的来说,StandardThreadExecutor是基于Java标准库中的线程池实现的,它通过ThreadPoolExecutor对象来管理线程池中的线程。线程池会根据预设的参数和线程状态来调度线程,以充分利用系统资源。同时,线程池还会对线程池中的线程进行监控和管理,以保证线程池的正常运行。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值