Certaines choses que vous devez savoir sur le pool de threads (2)

L'article a été publié pour la première fois sur le blog Youjian, tout le monde est invité à visiter le blog Youjian

Dans l'article précédent sur les pools de threads, certaines choses que vous devez savoir (1) Introduction générale du multi-threading et les scénarios dans lesquels les pools de threads ont leurs effets, et ont introduit certains paramètres de noyau internes, cet article présentera spécifiquement les pools de threads Mécanisme de fonctionnement. Pas grand chose à dire, il suffit d'aller au code!

exécuter()

C'est la méthode appelée par le pool de threads lors de l'exécution d'une tâche, voyons comment elle est implémentée

    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, noyau booléen)

Vous pouvez voir la méthode addWorker plusieurs fois dans le fichier execute () ci-dessus. Cette méthode a deux paramètres. Le premier est la tâche à exécuter et si le thread créé est le pool principal. Voici la méthode spécifique

    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);
        }

Ce qui précède est le processus d'ajout de threads et de tâches au pool de threads. On voit clairement qu'à la fin, si les conditions sont remplies, un nouveau thread est créé et s'il y a une tâche, la tâche sera exécutée en tant que première tâche du thread. Démarrez le thread, mais comment s'exécute-t-il après le démarrage du thread? Comment obtient-il les tâches dans la file d'attente des tâches?

runWorker (Worker w)

Vous pouvez appeler la méthode d'exécution de Worker lorsque vous voyez le début du thread de démarrage ci-dessus, examinons directement la méthode d'exécution

        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 ()

Jetons un œil à la méthode getTask (), comment obtenir des tâches de la file d'attente de tâches à exécuter

    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;
            }
        }
    }

Pour résumer

Lorsque le pool de threads exécute une tâche, il passe la tâche exécutable via execute () pour juger de l'état et du thread du pool de threads. S'il n'y a pas de problème, il rejoindra la file d'attente des tâches ou créera un thread pour exécuter la tâche en fonction de la situation. addWorker () crée un thread, démarre le thread et exécute les tâches entrantes. addWorker () entre runWorker () pour exécuter la tâche du thread cible après avoir appelé start . Si la tâche entrante est vide, la tâche sera retirée de la file d'attente des tâches pour être exécutée selon getTask () . L'ensemble du processus doit garantir qu'il n'y a aucun problème avec l'état du pool de threads.

Veuillez indiquer s'il y a une erreur de marquage.

Je suppose que tu aimes

Origine blog.csdn.net/qq_41762594/article/details/108674021
conseillé
Classement