java线程池源码走读(一)

  • execute源码
public void execute(Runnable command) {
// 任务的具体实现逻辑类不能为空       
 if (command == null)
            throw new NullPointerException();
        /*
        * 1 如果当前线程池中的线程数小于核心线程数corePoolSize,则创建一个新的线程,不过该线程是封装在Worker对象中
        * 2 addWorker方法中的第一个参数是该线程的第一个任务,而第二个参数就是代表是否创建的是核心线程
        * 3 如果当前线程池中的线程数已经满足了核心线程数corePoolSize,那么就会通过workQueue.offer()方法将任务添加到阻塞队列中等待执行
        * 4 如果线程数已经达到了corePoolSize且阻塞队列中无法插入该任务(比如已满),并且没有超过最大线程数maximumPoolSize,那么线程池就会再增加一个非核心线程来执行该任务
        * 5 如果确实已经达到了最大线程数,那么就拒绝这个任务
        * */
        int c = ctl.get();
        // 检查当前线程数是否达到了核心线程数
        if (workerCountOf(c) < corePoolSize) {
            // 未达到核心线程数,则创建新核心线程
            // 并将传入的任务作为该线程的第一个任务
            if (addWorker(command, true))
                // 添加核心线程成功则直接返回,如果没有添加成功,就该执行上面所说的3
                return;
            // 因为前面调用了耗时操作addWorker方法
            // 所以线程池状态有可能发生了改变,重新获取状态等信息
            c = ctl.get();
        }

        // 走到这里说明当前线程池中的线程数已经满足了核心线程数corePoolSize
        // 如果线程池当前状态是运行中就调用workQueue.offer方法将任务放入阻塞队列
        if (isRunning(c) && workQueue.offer(command)) {
            // 因为这个放入操作比较耗时,所以在放入成功之后,又做了一些列的校验操作
            int recheck = ctl.get();
            // 如果当前状态变成了非运行中(因为线程池的状态只能从小到大进行状态迁移,如果不是RUNNING那么肯定是至少处于SHUTDOWN状态,从该状态开始就不再接收新任务了),则将刚才放入阻塞队列的任务拿出,拿出成功后,直接拒绝这个任务
            // 疑问1
            if (! isRunning(recheck) && remove(command))
                reject(command);
            // 如果线程池中没有线程了,那就创建一个非核心线程
            // 这里没有看明白,一个线程也没有了,为什么创建的不是核心线程????
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        // 如果如果放入阻塞队列失败(如队列已满),则添加一个非核心线程
        // 如果不是RUNNING 这里为什么还能新建线程呢?而疑问1的地方不是运行态为什么要取出来拒绝,而不是跟这里一样建一个非核心线程进行处理???
        // 目测只能是addWorker逻辑中有非运行态的判断肯定也会创建不成功(具体可以查看addWorker源码注释)
        else if (!addWorker(command, false))
            // 如果添加线程失败(如已经达到了最大线程数),则拒绝任务
            reject(command);
    }
  • addWorker源码
// 改方法的主要作用是新建一个线程并启用,创建的线程被包装成了Worker对象
    private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);
            // 1 rs > SHUTDOWN 此时不再接受新的任务,直接返回添加失败
            // 2 rs = SHUTDOWN:firtTask != null 也会创建线程失败(这里也正好印证了我们在execute函数注解中留下的疑问),此时不再接受任务,但是仍然会执行队列中的任务
            // 3 线程池SHUTDOWN了不再接受新任务,但是此时队列不为空,那么还得创建线程把任务给执行完才行
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;
            /*
            * 走都这里,说明线程池状态或者为RUNNING,
            * 或者是SHUTDOWN状态,但是任务队列中还有任务,这个时候也是需要新建线程(当然这个线程究竟能不能新建成功,还需要后续一系列条件的限制)去执行完任务
            * */
            for (;;) {
                int wc = workerCountOf(c);
                /*
                * 线程数不能超过容量限制;如果是创建核心线程,也不能超过核心线程数,非核心线程也不能超过设置的最大值
                * */
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                // 原子的把线程数+1,这里只是把线程数+1,但是还没有真正的创建工作线程
                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
            }
        }

        // 走到这里就是真正的开始创建工作线程了
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            // 利用Worker构造方法中的线程池工厂创建线程,并把线程封装成Worker对象
            w = new Worker(firstTask);
            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)) {
                        // 检查线程是否仍然存活其实就是检查线程是否已启动(线程启动了就是存活了)
                        // 正常逻辑这里线程应该不是启动状态,只有在调用了start之后才是启动状态
                        // 所以如果这里是alive就报错
                        if (t.isAlive())
                            throw new IllegalThreadStateException();
                        workers.add(w); //添加到workers线程数组中
                        int s = workers.size();
                        // 整个线程池运行期间的最大并发线程数更新
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                // 添加到线程数组成功后,才启动新添加的线程
                if (workerAdded) {
                    // 启动线程的方法是调用Thread类的start()方法,该方法会在内部调用Runnable接口的run()方法,即会调用Worker对象中的run方法,
                    // 这是因为 w = new Worker(firstTask)时 其中的newThread时把this(即Worker本身传进去了)而worker实现了Runnable接口
                    // 所以执行start就是执行run就是执行Worker对象的runWorker(this)方法
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                // 启动失败就回退线程的创建,即释放掉线程
                addWorkerFailed(w);
        }
        return workerStarted;
    }

我们知道一个Thread在执行完其中的run方法之后就会退出,线程的生命周期也就结束了,那线程池中的线程是如何做到复用的呢?我们来看以下Worker类中的runWorker(this)方法。该方法会在线程启动时被调用(具体调用逻辑请看addWorker的源码注释)

  • runWorker(this)源码分析
final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        //在构造Worker对象的时候,会把一个任务添加进Worker对象中作为新增线程的第一个任务来执行
        Runnable task = w.firstTask;
        //已经将该任务拿出来进行执行,则需要将该worker对象 即线程池中的线程对象持有的任务清空
        w.firstTask = null;
        //将AQS锁资源的状态有-1变成0,运行该线程允许进行中断
        w.unlock(); // allow interrupts
        //用来判断执行任务的过程中,是否出现了异常,默认是异常退出
        boolean completedAbruptly = true;
        try {
            // 这里先大概说一下getTask的大概逻辑,核心线程会一直阻塞等待任务队列中有任务
            // 非核心线程会在等待keepAliveTime的超时时间之后,如果任务队列中还没有任务就会获取到null
            // 然后退出whie循环,进入到finally中 执行processWorkerExit结束调线程
            while (task != null || (task = getTask()) != null) {
                //给该线程加锁,一个线程只处理一个任务
                w.lock();
                // 线程池是否是STOP状态
                // 如果是,则确保当前线程是中断状态
                // 如果不是,则确保当前线程不是 中断状态
                // 中断对正在运行的线程不起作用,只对阻塞的线程起作用,这里具体怎么用??????
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    //扩展使用,在执行任务的run方法之前执行;前置钩子
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        // 执行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;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            // 正常执行完任务
            completedAbruptly = false;
        } finally {
            //所有的任务都处理完后,或者执行任务的过程中出现了异常
            processWorkerExit(w, completedAbruptly);
        }
    }
  • 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(); //Worker总数减1,返回null 外层的runWorker就会退出循环,进入销毁线程的逻辑
               return null;
           }

           int wc = workerCountOf(c);

           // Are workers subject to culling?
           // allowCoreThreadTimeOut:是否允许core Thread超时,默认false
           // workerCount是否大于核心核心线程池

           // 该标志的含义是:是否需要等待keepAliveTime时间再去队列中获取
           // 也就是说如果队列中没有任务,可以等待keepAliveTime时长,如果还没有任务的话,那么则返回null
           boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

           // 如果设置了allowCoreThreadTimeOut,那么大于coreThread(核心线程)就会有会有超时时间(即keepAliveTime)的限制,
           // 即如果当前的WorkerCount大于corePoolSize的话,那么超过的这些线程都会有超时的限制
           //1如果线程数超过最大限制,直接线程数-1,然后返回null(该线程就会销毁)
           //2如果启用了非核心线程超时设置,那么当获取任务超时并且线程池中有多于1个线程,就返回null并回收此线程
           //3如果启用了非核心线程超时设置,那么当获取任务超时 并且任务队列为空,就减1个线程(如果只有一个线程就会减失败)
           // 仍热会继续该循环,也就是说线程池中始终会至少有一个线程
           if ((wc > maximumPoolSize || (timed && timedOut))
               && (wc > 1 || workQueue.isEmpty())) {
               if (compareAndDecrementWorkerCount(c))
                   return null;
               continue;
           }

           try {
               
               // 如果启用了非核心线程超时设置,就使用poll获取任务,如果队列中没任务,该方法会获取超时,就会在下次循环中返回null
               // 核心线程会使用take方法获取任务,take方法在任务队列中没有任务时,会阻塞。
               Runnable r = timed ?
                   workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                   workQueue.take();
               if (r != null)
                   //判断任务不为空返回任务
                   return r;
               //获取一段时间没有获取到,获取超时
               timedOut = true;
           } catch (InterruptedException retry) {
               timedOut = false;
           }
       }
   }

你可能感兴趣的:(java线程池源码走读(一))