浅谈ThreadPoolExecutor

转载:https://www.cnblogs.com/trust-freedom/p/6681948.html

ThreadPoolExecutor#execute(Runnable command)
这里只分析能够被线程池执行的情况下。command会被包装成ThreadPoolExecutor#Worker对象

   Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

Worker实现了Runnable接口,通过上述构造函数可以看出通过线程工厂生产出来的线程执行的Runnable并非是command,而是command的包装对象Worker。(getThreadFactory().newThread(this)
所以该线程执行时会执行worker对象的run方法

Worker#run

 public void run() {
            runWorker(this);
        }

 简化版的runWorker方法。

  final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    while (task != null || (task = getTask()) != null) {
      task.run();
    }
  }

可以看出task即一开始的command,最终执行的就是command的run方法。执行前会将w.firstTask置为null,所以当再次while循环时前一个条件就不成立了。那么接着看第二个条件getTask

 简化版的getTask方法。

  private Runnable getTask() {
    for (;;) {
      int c = ctl.get();
      int wc = workerCountOf(c);
      boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
      Runnable r = timed ?
              workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
              workQueue.take();
      if (r != null)
        return r;
    }
  }

这里不考虑核心线程过期。所以timed的含义就是工作线程是否大于核心线程,是的话则通过指定超时时间获取队列头数据(是代表大于核心线程数的线程,所以当队列中没有数据时,大于核心线程数的线程会在一段时间后被销毁)。否的话通过take获取对列头数据。我们知道如果对列为空的话,take方法会一直阻塞(否代表核心线程,所以当队列中没有数据时,核心线程会被阻塞)。

从runWorker和getTask两个方法可以看出:

  • 线程池阻塞的原因就是核心线程阻塞在了空的队列上,所以需要shutDown,但如果线程池没有执行过任何任务,也就是没有一个核心线程,则不需要shutDown。(这里不包含线程池可以提前启动核心线程而不执行任务)
  • 一开始添加进线程池的核心线程,并不是一直是核心线程。而是根据该线程执行完当前任务后当前的工作线程是否大于核心线程来决定的。
  • 非核心线程的存活时间即为该线程执行完当前任务后在空队列阻塞的超时时间

猜你喜欢

转载自blog.csdn.net/sinat_33472737/article/details/114633158