线程池源码解析4.runWorker()

worker总流程图

在这里插入图片描述

1.runWorker()

大体流程:

先会执行当前的worker内部的任务,执行任务时需要加锁 (worker内部的锁),如果执行内部任务成功时会尝试去队列中拿任务继续执行(可能会被阻塞)。当执行任务失败时会将当前的worker从workers集合中删除。

	
	// worker 就是创建的worker(Runnable)
	final void runWorker(Worker w) {
    
    
        //就是worker内部的Thread属性
        Thread wt = Thread.currentThread();
        //将任务赋值给task
        Runnable task = w.firstTask;
        w.firstTask = null;
        
        /*
         *   这里不是解锁操作,这里是为了设置state = 0 以及 ExclusiveOwnerThread = null.因为起始状态state = -1, 
         *   不允许任何线程抢占锁,这里就是初始化操作。
         */   
        w.unlock(); 
        
        /*
         * 表示是否突然退出标志位,
         * true->  发生异常了,当前线程突然退出,后面会做处理
         * false-> 正常退出  
         */
        boolean completedAbruptly = true;
        try {
    
    
            
            /*
             *  条件一:
             *  task != null 值得就是firstTask是不是NULL,不是NULL执行循环体
             *   
             *  条件二:
             *  getTask()方法就是当前线程从BlockingQueue中获取任务,此方法会阻塞。
             *  getTask() == null 当前线程需要执行结束逻辑
             */ 
            while (task != null || (task = getTask()) != null) {
    
    
                /*
                 * 加锁 设置独占锁为当前线程 防止其他线程将当前线程中断。
                 * 为什么要设置独占锁,shutdown时会判断当前worker状态,根据独占锁是否空闲来判断当前worker是否正在工作。
                 */
                w.lock();
                
   /*
    *  这里有两个作用:
    *  1、线程池处于STOP/TIDYING/TERMINATION状态时需要设置线程的中断标志位
    *  2、强制刷新标志位,通过Thread.interrupted()方法,因为有可能上一次执行task时,当先线程的中断标志位被设置为了true,
    *  且没有处理,这里就需要处理一下。
    */
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    //设置中断标志位为true。
                    wt.interrupt();
                
                try {
    
    
                    //钩子方法,留给子类实现
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
    
    
                        /*
                         * task可能是FutureTask(通过线程池的submit()提交的方法),
                         * 调用run方法执行业务逻辑。
                         */
                        task.run();
                    } catch (RuntimeException x) {
    
    
                        thrown = x; throw x;
                    } catch (Error x) {
    
    
                        thrown = x; throw x;
                    } catch (Throwable x) {
    
    
                        thrown = x; throw new Error(x);
                    } finally {
    
    
                        //钩子方法,留给子类实现
                        afterExecute(task, thrown);
                    }
                } finally {
    
    
                    //将task置为NULL
                    task = null;
                    //更新worker完成任务的数量
                    w.completedTasks++;                    
                    /*
                     * worker处理完一个任务后,会释放掉独占锁
                     * 1.正常情况下,会再次回到getTask()获取任务(外面是一个while循环) 
                     * 2.任务执行出异常,
                     */
                    w.unlock();
                }
            }
            
            /*
             * 什么情况下,回来到这里?
             * 1.当getTask()返回NULL时,说明当前线程应该执行退出逻辑了。。
             */
            completedAbruptly = false;
        } finally {
    
    
            
            /*  
             *  task.run()内部抛出异常,直接从w.unlock那里跳到这一行。
             *  正常退出:completedAbruptly = false
             *  异常退出:completedAbruptly = true。
             */
            processWorkerExit(w, completedAbruptly);
        }
    }

2.getTask()

	/*
	 *  什么情况下会返回NULL?
 	 *  1.当前线程状态 >= STOP,成立说明当前的状态最低也是STOP,一定要返回NULL。
	 *  2.线程池状态是SHUTDOWN,并且队列中没有元素
	 *  3.线程池中的线程数量超过最大限制时,会有一部分线程返回NULL。
	 *  4.线程池中的线程数超过了corePoolSize时,会有一部分线程 获取任务超时后 返回NULL。
	 */

	private Runnable getTask() {
    
    
     
     	//表示当前线程获取任务是否超时,默认是false,true表示已超时。
        boolean timedOut = false; 
		
     	//自旋
        for (;;) {
    
    
            //获取最新的ctl的值
            int c = ctl.get();
            
            //获取线程池当前运行状态
            int rs = runStateOf(c);

            /*
             * 这里判断如果说线程池状态是非Running状态 && (队列中没有任务了 || 线程池当前最低状态也是STOP)
             * 就会使用CAS的方式将ctl值-1,即减少一个工作线程数。最后直接返回NULL。
             */ 
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
    
    
                decrementWorkerCount();
                return null;
            }

            /*
             * 执行到这里,有几种情况?
             * 1.线程池是Running状态,
             * 2.线程池是SHUTDOWN状态,但是队列中还有任务。(此时可以创建线程)
             */
            
            //获取线程池中的线程数量
            int wc = workerCountOf(c);

            /*
             * timed表示当前线程在从队列中获取任务的时候是否有超时时间。
             * poll(xx, xx)获取时可以设置超时时间,take()没有超时时间。
             * 
             *  设置此参数的主要依据就是,判断allowCoreThreadTimeOut是否允许核心线程超时被回收(注意:核心线程与非核心线程没有任何区别,
             * 	 只是通过设置的核心线程数量最后保留指定的线程),如果此参数为true,表示核心线程允许被回收,以及当前线
             *   程数的数量已经大于了核心线程数,说明当前线程当获取任务超时时一定可以被回收。 
             */           
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
			
            
            /*
             * 判断当前线程是否达到了回收的标准,
             * 当获取任务超时并且核心线程可以被回收并且 当前线程池线程数量大于1并且队列中没有任务了,当前线程就达到回收标准了,
             * 当确实需要回收时才会被回收。
             */
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
    
    
                
                /*
                 * CAS方式减去一个线程数,成功后返回NULL.
                 * CAS有可能会失败,为什么会失败?
                 *  1.其他线程先你一步退出
                 *  2.线程池状态发生了变化。
                 */
                if (compareAndDecrementWorkerCount(c))
                    //直接返回 null。
                    return null;
                
                /*
                 * 如果CAS失败,再次自旋,timed就有可能是false了,因为当前线程CAS失败,
                 * 很有可能是因为其他线程成功退出导致的,再次自旋时检查发现,当前线程就可能不属于回收范围了。
                 */
                continue;
            }
            try {
    
    
                /*  获取任务的逻辑
                 *  根据timed的值,判断去队列中获取任务是使用带超时时间的还是不带超时时间的。 (注意:如果获取不到任务都会阻塞)
                 */
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                
                //获取到了任务
                if (r != null)
                    return r;
                //说明当前线程超时了。继续进行自旋。
                timedOut = true;
            } catch (InterruptedException retry) {
    
    
                timedOut = false;
            }
        }
    }

3.processWorkerExit()

	/*
	 * @param w : 表示当前worker
	 * @param completedAbruptly:为true,表示当前worker是因为任务出异常退出的,
	 * false表示当前worker是因为没有获取到任务。
	 */
	private void processWorkerExit(Worker w, boolean completedAbruptly) {
    
    
        /*
         * 条件成立:代表当前worker是发生异常退出的,即在执行任务期间向上抛出异常了。
         * 异常退出时,ctl计数并没有-1,
         */
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            //CAS将ctl - 1;
            decrementWorkerCount();
		
        final ReentrantLock mainLock = this.mainLock;
        //加锁(全局锁)
        mainLock.lock();
        
        try {
    
    
            //将当前worker完成的任务总数累加到全局总数上
            completedTaskCount += w.completedTasks;
            //将当前worker从线程池(workers就是一个HashSet)移除
            workers.remove(w);
        } finally {
    
    
            //解锁
            mainLock.unlock();
        }
		
        //后面详细讲
        tryTerminate();
		
        //获取ctl的值
        int c = ctl.get();
        
        //条件成立:当前线程池状态为running 或者 shutdown状态。
        if (runStateLessThan(c, STOP)) {
    
    
            
            //这里成立说明当前线程是正常退出。
            if (!completedAbruptly) {
    
    
                
                //min表示线程池最低可以持有的线程数量
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                
                /*
                 * 线程池状态:RUNNING SHUTDOWN
                 * 条件一:mid == 0成立
                 * 条件二:队列不空
                 * 说明队列中还有任务,起码得留一个线程。
                 */
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1; //赋值为1
                
                /*
                 * 条件成立: 线程池中还拥有足够的线程时,后续就不需要调用addWorker()了
                 * 考虑一个问题: workerCountOf(c) >= min  -> (0 >= 0)?
                 *  当线程池中的核心线程数是可以被回收的情况下,会出现这种情况,这种情况下,
                 *  当前线程池中的线程数会变为0,下次在提交任务时,会再创建线程。 
                 */
                if (workerCountOf(c) >= min)
                    return; 
            }
            
            /*
             * 来到这里的情况
             * 1.completedAbruptly为true,表示执行任务时异常退出了。
             * 2.当队列中还有任务时,起码留一个线程,这里就会创建一个线程(worker)。
             * 3.当前线程数 < corePoolSize,此时会创建线程,维护corePoolSize。
             */
            addWorker(null, false);
        }
    }

Guess you like

Origin blog.csdn.net/qq_46312987/article/details/121615560