ThreadPoolExecutor.Worker内部类分析

本文深入分析了ThreadPoolExecutor线程池中的Worker内部类,解释了线程池如何从最大线程数逆向减少到核心线程数的过程。通过Worker内部类的运行机制,了解线程何时及如何被回收。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇文章分析了ThreadPoolExecutor线程池类,明白了池中的线程和任务队列是如何工作的,文章链接:http://blog.youkuaiyun.com/wangjiang87/article/details/78672373,这篇文章分析下Worker内部类来了解下池中的线程数量是如何逆向减少的。 Worker是实现了Runnable接口,主要的成员变量:
Runnable firstTask:执行run方法中的循环中第一个要执行的任务,有可能为null
Thread thread:work就是执行在这个线程当中。 这个变量可以结合addIfUnderCorePoolSize和addThread两个方法去了解。如下:

private boolean addIfUnderCorePoolSize(Runnable firstTask) { 
    Thread t = null; 
    mainLock.lock();
    try { 
        if (poolSize < corePoolSize && runState == RUNNING) 
            t = addThread(firstTask); 
    } finally { 
        mainLock.unlock();
     } 
     if (t == null) 
     return false; 
     t.start(); 
     return true; 
 } 
 private Thread addThread(Runnable firstTask) {
     Worker w = new Worker(firstTask); 
     Thread t = threadFactory.newThread(w);
     if (t != null) {
         w.thread = t;
         workers.add(w);
         return t; 
     } 
 } 

首先,addIfUnderCorePoolSize方法中会调用addThread方法获得一个线程,然后启动这个线程,那获得的线程是哪个呢?看addThread方法。步骤如下:

  1. Worker w = new Worker(firstTask) 新建一个Worker内部类实例对象,入参是Runnable,这个Runnable也就是我们需要线程池帮我们执行的任务,把这个任务赋值给Worker内部类中的firstTask变量;
  2. Thread t = threadFactory.newThread(w);创建一个需要执行worker内部类的一个线程,别忘了woker内部类本身也实现了Runnable接口
  3. 将第二步创建的线程引用赋值给内部类中的thread变量,使worker和线程关联起来,以后缓存的是worker实例对象,从而也就将线程缓存了起来
  4. 将worker放到ThreadPoolExecutor的workers中缓存起来,也就是将线程放到池中
  5. poolSize加1,记录池中的线程数量
  6. 上面5步执行完相当于addThread方法执行结束,将(2)中创建的线程对象返回给addIfUnderCorePoolSize方法,在addIfUnderCorePoolSize中调用t.start()启动池中的线程来执行任务。t.start()启动之后,执行的是哪个实例的run方法呢?由第(2)步可以看出,是worker实例的,那就接着看worker内部类中的run方法的实现:
 public void run() { 
      try { 
          Runnable task = firstTask; 
          firstTask = null;
          while (task != null || (task = getTask()) != null) {
               runTask(task); task = null; 
           }
        } finally { 
            workerDone(this); 
        }
   } 

run方法就是一个循环去执行任务的过程,先执行第一次创建worker实例时传入的任务,执行完之后,从workQueue任务队列中取任务继续执行,直到队列中任务为空,然后执行workerDone方法。 runTask方法很简单,就是执行了task.run()方法,我们主要来看下getTask和workerDone方法。 先看getTask方法:

 Runnable getTask() {
        for (;;) {
            try {
                int state = runState;
                if (state > SHUTDOWN)
                    return null;
                Runnable r;
                if (state == SHUTDOWN)  // Help drain queue
                    r = workQueue.poll();
                else if (poolSize > corePoolSize || allowCoreThreadTimeOut)
                    r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);
                else
                    r = workQueue.take();
                if (r != null)
                    return r;
                if (workerCanExit()) {
                    if (runState >= SHUTDOWN) // Wake up others
                        interruptIdleWorkers();
                    return null;
                }
                // Else retry
            } catch (InterruptedException ie) {
                // On interruption, re-check runState
            }
        }
    }

getTask方法主要是从workQueue中取任务,然后执行。取任务也分了三种情况:

  1. state == SHUTDOWN;
  2. poolSize > corePoolSize || allowCoreThreadTimeOut;
  3. 其他情况。

    前两种使用poll方法,不阻塞或阻塞指定时间,有就返回,没有就返回null,第三种使用了take方法,队列中没有任务了就阻塞,直到有任务了才返回。
    第二种情况:poolSize > corePoolSize || allowCoreThreadTimeOut 当池中的线程数量大于核心线程数或者核心线程也要求有过期时间时,从队列中获取任务时,超过超时时间没有获取到任务,则返回null,目的就是让超过corePoolSize的线程或者是有核心线程超时时间也生效的核心线程这类线程如果空闲了指定时间,就结束生命周期,使池中的线程数量达到核心线程(若核心线程超时时间生效时即allowCoreThreadTimeOut==true,没有任务时,池中线程数量最终会变成0)
    第三种情况:当前两种情况都不是时,就使用take方法阻塞,直到队列中有任务时再返回。可以想象到,如果队列中没有元素,池中的所有核心线程都会在它的take方法处阻塞。
    getTask方法分析完,也就清楚了worker内部类中的run方法循环结束的条件了:就是getTask方法获取任务的前两种情况下,会结束循环,从而执行到finally块中的workerDone方法。
    workDone方法:

void workerDone(Worker w) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            completedTaskCount += w.completedTasks;
            workers.remove(w);
            if (--poolSize == 0)
                tryTerminate();
        } finally {
            mainLock.unlock();
        }
    }

主要完成的事情有:(1)记录完成的总任务数;(2)将线程从workers缓存中移除;(3)poolSize减1
分析完Worker内部类,也就知道了线程数量是怎么从maximumPoolSize减少到corePoolSize数量的线程的逆向流程的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值