Java线程池以及线程池监控(II)

本文部分来源自Java线程池实现原理及其在美团业务中的实践

上一篇说到了execute的执行过程,接下来讲一讲线程创建的过程,即addWorker(Runnable firstTask, boolean core)

  private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                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 {
            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)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }
复制代码

addWorker两个参数,firstTask表示线程创建成功后执行firstTask,core表示创建是否是核心线程池里的线程 1.有两层死循环可以理解为自旋,外层的自旋,先获取当前的线程池状态rs,如果当前rs的状态大于等于SHUTDOWN的或者SHUTDOWN状态且队列没有任务了,创建失败,如果rs的状态为RUNNING,则进入第二层自旋 2.获取当前的线程数wc,判断wc是否大于CAPACITY,CAPACITY为5亿多基本不可能,接着再根据core的值判断wc是要大于corePoolSize还是maximumPoolSize,满足其中一个条件,就创建失败。

                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
复制代码

3.如果线程数符合要求就会通过CAS的方式增加当前线程数量的值,如果CAS成功则跳出两层自旋

                if (compareAndIncrementWorkerCount(c))
                    break retry;
复制代码

,如果失败,再次判断下当前的状态是否跟之前的rs一致,如果不一致回到最外层的循环,重新走1步骤,如果一致,则重新走2步骤

                if (runStateOf(c) != rs)
                    continue retry;
复制代码

4.线程池中的线程都被包装成工作线程Worker,线程在Worker里面创建。创建一个工作线程,把firstTask传进去

    private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
        /**
         * This class will never be serialized, but we provide a
         * serialVersionUID to suppress a javac warning.
         */
        private static final long serialVersionUID = 6138294804551838833L;

        /** Thread this worker is running in.  Null if factory fails. */
        final Thread thread;
        /** Initial task to run.  Possibly null. */
        Runnable firstTask;
        /** Per-thread task counter */
        volatile long completedTasks;

        /**
         * Creates with given first task and thread from ThreadFactory.
         * @param firstTask the first task (null if none)
         */
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
    }
复制代码

拿到Worker里的线程

final Thread t = w.thread;
复制代码

如果t不为空,就会获取锁,获取是为了一些操作能够安全的更新如工作集合的添加和largestPoolSize的修改,获取到锁后,获取当前线程池的状态rs,判断rs是否符合状态,符合的话再判断t是否已经启动,如果启动抛出异常,否则把该工作线程添加到工作线程的集合中,并修改largestPoolSize,紧接着释放锁。然后启动线程

 if (workerAdded) {
     t.start();
     workerStarted = true;
 }
复制代码

如果线程启动失败,则会把刚刚的工作线程从工作线程集合中删除,并把线程数量减一

finally {
            if (! workerStarted)
                addWorkerFailed(w);
 }
 ----------
 private void addWorkerFailed(Worker w) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        if (w != null)
            workers.remove(w);
        decrementWorkerCount();
        tryTerminate();
    } finally {
        mainLock.unlock();
    }
}
复制代码

上面用的finally的原因是因为在之前的两层循环中,已经把线程数量加一了,所以无论如何只要线程没启动成功就得要把线程数量减一,以确保数量正确。

讲完了如何添加工作线程的,现在来讲一讲工作线程是如何运行起来的。工作线程中的运行是执行runWorker()

        public void run() {
            runWorker(this);
        }
----------------------------------
    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);
        }
    }        
复制代码

线程执行前先解锁,确保能够被中断,接着使用while一直循环从getTask()获取任务,获取到任务后,先把线程上锁,然后判断当前线程的状态是否是STOP,如果是STOP,则中断当前的线程。在运行真正的线程run时前有一个beforeExecute,运行完后有一个afterExecute,这两个方法都是空方法,留着给开发人员实现,可以对线程的运行前面做一下操作,当线程运行完后,当前工作线程的完成任务数加一,同时线程释放锁

finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
复制代码

如果获取不到任务,则回收工作线程

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();
                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;
            }
        }
    }
复制代码

1.设置一个变量timeOut来判断上一次获取任务是否超时, 2.接着获取当前的线程池状态rs,判断当前线程池状态是否为STOP或者是SHUTDOWN且队列为空,如果是减少线程数量-1,返回null。 3.否则获取当前的线程数量wc,同时设置一个变量timed判断是否核心线程允许超时,紧接着判断 wc 是否大于 maximumPoolSize ,或者上一次获取任务超时且队列为空,符合就减少线程数量,返回null 4.最重要的一步

            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
复制代码

根据timed来判断是阻塞的获取任务一直等待直到有任务,还是根据keepAliveTime等待一定时间如果没有获取到任务就返回null,如果获取到任务就返回任务,获取不到任务任务就把timedOut 置为true进行下一次的循环,同时如果阻塞线程被中断了,这里会捕获到,把timedOut = false进行下一次的循环。

在这里额外介绍两点 1.为什么要判断wc > maximumPoolSize,这是因为有可能使用了 setMaximumPoolSizemaximumPoolSize调小了,导致目前的线程数量超过最大值,所以要把超出的部分减去同时回收超过的部分 2.timed = allowCoreThreadTimeOut || wc > corePoolSize;,在线程池中使用 allowCoreThreadTimeOut 来控制核心线程池的线程是否也应该被超时回收,如果为true则核心线程池也需要,为false则超过核心线程池数量的线程才会被超时回收

猜你喜欢

转载自juejin.im/post/7033410796248006669