Some things you need to know about the thread pool (2)

The article was first published on Youjian blog, everyone is welcome to visit Youjian blog

In the previous article about thread pools, some things you need to know (1) General introduction of multi-threading and the scenarios in which thread pools have their effects, and introduced some internal core parameters, this article will specifically introduce thread pools Operating mechanism. Not much to say, just go to the code!

execute()

This is the method called by the thread pool when executing a task, let’s see how it is implemented

    public void execute(Runnable command) {
    
    
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         * 
         * 1、如果当前线程数小于核心线程数,那么就创建一个线程,并且
         * 将该任务作为这个线程的第一个任务,调用addWorker()方法
         * 时会原子性的检查runStatus和workerCount,如果addWorker失败,则返回
         * false。
         * 
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         * 
         * 2.如果任务能够被加入任务队列,然后需要二次检查是否增加一个线程
         * 到池中,可能上一个存在的线程已经死亡了 ,而且当线程池已经不处于运行状态的时候,
         * 回滚添加到工作队列中的任务。
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         * 3. 如果任务队列已经满了,在不超过最大池的前提下将会去创建一个线程去做这个任务,
         * 如果执行失败,执行拒绝策略。
         * 
         */
        //获取当前线程池的状态和线程数
        int c = ctl.get();
        //如果线程数小于核心线程数
        if (workerCountOf(c) < corePoolSize) {
    
    
        	//则执行创建线程并执行任务的操作
            if (addWorker(command, true))
            	//成功后返回
                return;
            //如果失败了则重新获取状态值和线程数
            c = ctl.get();
        }
        //线程池是否还是RUNNING状态,如果是的话就往工作队列中添加任务
        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(Runnable firstTask, boolean core)

You can see the addWorker method multiple times in the execute() above. This method has two parameters. The first is the task to be executed and whether the thread created is the core pool. The following is the specific method

    private boolean addWorker(Runnable firstTask, boolean core) {
    
    
    	//先为下面的for循环进行一个标识
        retry:
        for (;;) {
    
    
        	//获取当前线程池的状态和线程数
            int c = ctl.get();
            //获取当前线程池的状态
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            //这里进行了一个判断,首先判断当前的线程池状态是否大于SHUTDOWN
            //我们明白SHUTDOWN = 0, RUNNING < 0,所以如果rs >= SHUTDOWN就代表了不是可运行状态
            //代表了不允许往线程池中添加新的任务,如果 > SHUTDOWN,则直接退出,如果 == SHUTDOWN
            //那么就会判断,如果当前任务为空或者任务队列不为空的时候,会继续执行。
            //任务为空代表了仅仅只是创建一个线程给予执行,队列不为空是在SHUTDOWN状态下继续执行完队列剩余的任务。 
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                //如果不满足上述,返回false
                return false;
			//如果满足上述的要求,那么就是允许添加并执行任务了,设置一个for死循环,用于cas判断跳出
            for (;;) {
    
    
            	//重新获取当前线程数
                int wc = workerCountOf(c);
                //如果当前的线程数已经达到最大值,或者
                if (wc >= CAPACITY ||
                	//是否大于核心池大小或者最大池大小(看传入的core判断)
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    //如果大于,说明线程数满了,直接返回
                    return false;
                //通过cas的方式对线程数c进行添加
                if (compareAndIncrementWorkerCount(c))
                	//如果成功,直接跳出最外部循环
                    break retry;
                //如果失败了,重新获取已经改变的线程数以及状态值
                c = ctl.get();  // Re-read ctl
                //如果线程池状态值已经改变了,重新循环外部循环,重新判断线程池状态情况
                if (runStateOf(c) != rs)
                	//重新循环
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
                //不然则重新进行内部循环,重新进行cas的判断增加
            }
        }
		//能执行到这里,代表上述流程已经走完,线程数名义上已经成功添加,下面就是真正添加线程的过程
		//设置一个判断线程是否启动
        boolean workerStarted = false;
        //设置一个判断线程是否增加
        boolean workerAdded = false;
        Worker w = null;
        try {
    
    
        	//根据任务生成一个工作线程,内部是指定的任务以及线程工厂创建的线程
            w = new Worker(firstTask);
            //将线程实体的线程进行取出来
            final Thread t = w.thread;
            //如果生成的线程不为null
            if (t != null) {
    
    
            	//获取线程池的主锁
                final ReentrantLock mainLock = this.mainLock;
                //进行加锁,这时其他线程进行下述步骤的时候将会进行阻塞
                mainLock.lock();
                try {
    
    
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    //进入锁后重新获取线程池状态,防止获取锁前进行退出以及在线程工厂处执行失败
                    int rs = runStateOf(ctl.get());
					//如果状态是RUNNING或者是SHUTDOWN但执行的任务为空时
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
    
    
                        //如果线程已经非存活,则异常
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        //将工作线程添加到工作线程的集合,仅仅在锁定时可以访问
                        workers.add(w);
                        //获取当前工作线程集合的大小
                        int s = workers.size();
                        if (s > largestPoolSize)
                        	//设置能达到的最大线程大小
                            largestPoolSize = s;
                        //成功创建后,将判断添加的值设为true
                        workerAdded = true;
                    }
                } finally {
    
    
                	//最后进行解锁
                    mainLock.unlock();
                }
                //如果成功添加线程
                if (workerAdded) {
    
    
                	//将线程进行启动
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
    
    
        	//如果没有成功
            if (! workerStarted)
            	//将线程从线程集合中移除,并cas减少ctl的值
                addWorkerFailed(w);
        }
        return workerStarted;
    }

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

The above is the process of how to add threads and tasks to the thread pool. It can be clearly seen that at the end, if the requirements are met, a new thread is created, and if there is a task, the task will be executed as the first task for the thread. Start the thread, but how does it execute after the thread is started? How does it get the tasks in the task queue?

runWorker(Worker w)

You can call the run method of Worker when you see the start of the start thread above, let’s take a look at the run method directly

        public void run() {
    
    
        	//将worker委托给runWorker运行
            runWorker(this);
        }
	
	//真正执行线程逻辑的代码,传入线程实体
    final void runWorker(Worker w) {
    
    
    	//获取当前的执行线程
        Thread wt = Thread.currentThread();
        //以及之前封装进来的任务
        Runnable task = w.firstTask;
        //将worker封装的任务重新设为null
        w.firstTask = null;
        //worker的构造函数中setState(-1)禁止线程中断,所以这里unlock允许中断
        w.unlock(); // allow interrupts
        //用于标识是否突然终止
        boolean completedAbruptly = true;
        try {
    
    
        	//如果需要执行的任务不为null则优先执行现有的任务,
        	//不然则调用getTask()获取任务队列中的任务,详细在下面解读,获取的任务不为null
        	//则继续执行
            while (task != null || (task = getTask()) != null) {
    
    
            	//设置禁止中断,具体下面解析
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                //首先进行一个判断,判断当前的线程池是否处于STOP,TIDYING,TERMINATION
                //如果处于而线程未中断,则中断线程
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
    
    
                	//在执行前设定一个方法,可以用于后来重写逻辑,暂时没有执行任何逻辑
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
    
    
                    	//执行真正的任务
                        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 {
    
    
                	//最后设置任务为null
                    task = null;
                    //完成的任务数+1
                    w.completedTasks++;
                    //解锁允许中断
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
    
    
        	//清理线程池中的数据。
            processWorkerExit(w, completedAbruptly);
        }
    }

getTask()

Let’s take a look at the getTask() method, how to get tasks from the task queue to execute

    private Runnable getTask() {
    
    
    	//判断是否已经线程超时,如果有设置超时的话
        boolean timedOut = false; // Did the last poll() time out?
		//设置一个死循环,进入取任务
        for (;;) {
    
    
        	//获取当前线程池的状态和线程数
            int c = ctl.get();
            //获取线程池的状态
            int rs = runStateOf(c);
			//如果当前的线程池状态是STOP及以上,则开始清除线程
			//如果是SHUTDOWN但是队列为空,也开始清除线程
            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
    
    
            	//减少可用线程数量,内部是do while循环,清除全部
                decrementWorkerCount();
                //返回空
                return null;
            }
			//获取可用的线程数
            int wc = workerCountOf(c);
			//这里判断是否需要使用超时
			//allowCoreThreadTimeOut 如果为true则代表核心线程有超时时间,
			//或者线程数大于核心线程数,则其他线程都会需要使用超时
            // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
			//如果线程数已经大于最大池设定的线程数 或者 有设定超时并且已经超时并且线程数大于1或者任务队列为空不需要进行时 
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
    
    
                //执行减少线程,清除一个
                if (compareAndDecrementWorkerCount(c))
                	//结束本段线程
                    return null;
                //再次进入循环重新判断
                continue;
            }
			//如果上述情况,如超时,线程池状态,线程数都没有问题,则执行下面的获取任务逻辑
            try {
    
    
            	//如果有设置超时时间,那么就在超时时间内从任务队列中取任务
            	//如果没有设置超时时间,则take()直到有任务到来
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                //如果获取到的任务不为空
                if (r != null)
                	//返回任务
                    return r;
				//设置超时,代表线程在规定的时间内没有从任务队列中取到任务。
				//然后又会进行循环,判断超时时间
                timedOut = true;
            } catch (InterruptedException retry) {
    
    
            	//设置异常就设置成未超时继续循环
                timedOut = false;
            }
        }
    }

to sum up

When the thread pool is executing a task, it passes in the Runnable task through execute() to judge the state and thread of the thread pool. If there is no problem, it will join the task queue or create a thread to execute the task according to the situation. addWorker() creates a thread, starts the thread and executes the incoming tasks. addWorker() enters runWorker() to execute the task of the target thread after calling start . If the incoming task is empty, the task will be taken out of the task queue for execution according to getTask() . The entire process needs to ensure that there is no problem with the state of the thread pool.

Please point out if there is any marking error.

Guess you like

Origin blog.csdn.net/qq_41762594/article/details/108674021