zoukankan      html  css  js  c++  java
  • 线程池ThreadPoolExecutor工作原理

    前言

    工作原理

    如果使用过线程池,细心的同学肯定会注意到,new一个线程池,但是如果不往里面提交任何任务的话,main方法执行完之后程序会退出,但是如果向线程池中提交了任务的话,main方法执行完毕之后程序是不会自动退出的,是什么原理,或者说是什么原因导致任务提交到线程池之后任务执行完程序无法自动退出的呢?下面就让我们趴开线程池的源码,一探究竟。

    我们直接从ThreadPoolExecutor的execute方法开始说起。线程提交到ThreadPoolExecutor执行分为三种情况,具体如下:

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        
        int c = ctl.get();
        //1、当前线程池中的线程数小于corePoolSize
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //2、当前线程池中的线程数大于corePoolSize,直接将任务放入工作队列
        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);
        }
        //3、如果入队失败,尝试启动新的线程,即此时工作队列已满,线程池中的线程数大于corePoolSize小于maxPoolSize
        else if (!addWorker(command, false))
            reject(command);//启动新的线程失败,执行拒绝策略
    }
    

    现在我们知道,将一个任务提交到ThreadPoolExecutor线程池执行分为三种情况,可以看到,三种情况下都有一个addWorker的动作,下面我们主要看看addWorker里面做了什么

    private boolean addWorker(Runnable firstTask, boolean core) {
        // 第一步,cas操作保证正确的增加任务数
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);
            
            // 如果当前线程池不处于RUNNING状态,则不能添加任务
            if (rs >= SHUTDOWN && // 如果线程池状态rs >= SHUTDOWN,也就是非RUNNING状态,此时不接受新任务
                ! (rs == SHUTDOWN && //rs == SHUTDOWN ,此状态不接受新任务
                   firstTask == null && 
                   ! workQueue.isEmpty())) // 工作队列不为空
                return false;
    
            for (;;) {
                // 获取任务数量
                int wc = workerCountOf(c);
                //如果线程数 大于等于CAPACITY 添加任务失败
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))//尝试增加任务数量
                    break retry;
                c = ctl.get();  // Re-read ctl
                // 如果当前的运行状态不等于rs,说明状态已被改变,返回第一个for循环继续执行
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }
        // 第二步 创建一个Worker,包装当前的任务,并启动该work中创建的线程,用于执行当前当前提交过来的任务
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);//新建一个worker,同时从ThreadFactory中创建一个新的线程
            final Thread t = w.thread;//
            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());
    
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        workers.add(w);//放入worker集合
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {//worker添加成功,启动任务
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }
    

    这里分为两步,首先使用cas操作保证成功增加workerCount,然后将创建一个worker,将worker添加人worker池,启动worker,返回任务添加成功

    Worker是ThreadPoolExecutor线程池的内部类,主要作为用户提交任务的包装,它继承自AbstractQueuedSynchronizer类并实现了Runnable接口,它的run方法很简单,直接调用runWorker方法,runWorker如下:

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            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
                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 {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }
    

    可以看到,在runWorker中使用了一个while循环,使用getTask去获取任务, getTask如下:

    private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?
    
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);
    
            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }
    
            int wc = workerCountOf(c);
    
            // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
    
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }
    
            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }
    

    getTask通过workQueue.take();方法获取任务,我们知道,blockingQueue的take方法是阻塞的,当队列为空时,会一直阻塞知道获取新的任务,到这里,我们便可以回答上面提到的问题了

    重新梳理一遍,用户向ThreadPoolExecutor线程池中添加任务时,ThreadPoolExecutor会创建一个Worker,用来包装并执行用户任务,Worker的run方法中采用while循环,通过getTask方法不断的取出工作队列中的任务执行,当任务队列为空时,take方法阻塞了线程,导致任务执行线程一直不会退出,所以用户想ThreadPoolExecutor线程池中提交任务之后程序不会自动结束,就是这个原理。

  • 相关阅读:
    好的学习资源
    对paper有用的idea
    斜杠青年
    简书随笔
    点云专业英文单词
    通过 UDP 发送数据的简单范例
    简单的聊天时范例(客户端)
    键盘输入
    简单的传输文件范例
    编写serversocket简单示例1
  • 原文地址:https://www.cnblogs.com/canmeng-cn/p/9332746.html
Copyright © 2011-2022 走看看