java 线程池源码分析

  • java线程池干了些什么事
    • 主要是维持一些常驻的线程,避免每次执行任务新建线程的开销
    • 将线程的执行和提交解耦,用户只用关心线程的提交,而不用关心线程的执行
  • 我们通常怎么使用线程池
ExecutorService threadPoolExecutor = Executors.newFixedThreadPool(2);
        Future<?> future =  threadPoolExecutor.submit(new Runnable() {
            @Override
            public void run() {
                int a = 1/0;
            }
        });
        future.get();
  • 从submit方法出发,看看都干了些什么事
    • submit方法是executorService接口的方法,在threadPoolExecutor的父类,abstractExecutorService中被实现。
public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }
  • 可以看到,先是将runnable转换成一个futureTask。然后执行。
  • 在threadPoolExecutor中
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.
         *
         * 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.
         *
         * 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.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        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);
    }
  • 如果当前worker的数量少于coreSize,直接新加一个worker, 否则,将任务加入队列中。
  • 看下worker的结构。
 /** 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);
        }

        /** Delegates main run loop to outer runWorker  */
        public void run() {
            runWorker(this);
        }
  • worker 本身是一个runnable的实例,同时包含一个thread变量。在worker方法被线程执行时,会调用到run方法,然后看下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是一个死循环,不断的从queue中取任务执行,由于是阻塞队列,队列中无任务时,阻塞等待。需要注意的是,如果这里task抛了异常,会抛出exception,然后执行workerExit逻辑
private void processWorkerExit(Worker w, boolean completedAbruptly) {
       if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
           decrementWorkerCount();

       final ReentrantLock mainLock = this.mainLock;
       mainLock.lock();
       try {
           completedTaskCount += w.completedTasks;
           workers.remove(w);
       } finally {
           mainLock.unlock();
       }

       tryTerminate();

       int c = ctl.get();
       if (runStateLessThan(c, STOP)) {
           if (!completedAbruptly) {
               int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
               if (min == 0 && ! workQueue.isEmpty())
                   min = 1;
               if (workerCountOf(c) >= min)
                   return; // replacement not needed
           }
           addWorker(null, false);
       }
   }
  • 如果woker因为异常退出了,那这个worker和worker内部维护的thread都会被销毁,从workset中移除之后,worker没有引用,等待被gc。同时,看是不是要补充新的worker。

ScheduledThreadPoolExecutor

  • ScheduledThreadPoolExecutor相比普通的threadPoolExecutor干了什么事呢,无非是一个任务,隔一定间隔重复执行。
  • 我们怎么使用ScheduledThreadPoolExecutor?
@Test
    public void scheduleExecutorService() {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);
        executorService.scheduleWithFixedDelay(()->{
            System.out.println("aa");
        },2,2,TimeUnit.SECONDS);
        executorService.scheduleAtFixedRate(()->{
            System.out.println("aa");
        },2,2,TimeUnit.SECONDS);
    }
  • 看下scheduleAtFixedRate和scheduleWithFixedDelay的区别是什么
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit) {
        if (command == null || unit == null)
            throw new NullPointerException();
        if (delay <= 0)
            throw new IllegalArgumentException();
        ScheduledFutureTask<Void> sft =
            new ScheduledFutureTask<Void>(command,
                                          null,
                                          triggerTime(initialDelay, unit),
                                          unit.toNanos(-delay));
        RunnableScheduledFuture<Void> t = decorateTask(command, sft);
        sft.outerTask = t;
        delayedExecute(t);
        return t;
    }

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit) {
        if (command == null || unit == null)
            throw new NullPointerException();
        if (period <= 0)
            throw new IllegalArgumentException();
        ScheduledFutureTask<Void> sft =
            new ScheduledFutureTask<Void>(command,
                                          null,
                                          triggerTime(initialDelay, unit),
                                          unit.toNanos(period));
        RunnableScheduledFuture<Void> t = decorateTask(command, sft);
        sft.outerTask = t;
        delayedExecute(t);
        return t;
    }

  • 两个方法的唯一区别是在构造ScheduledFutureTask时,scheduleWithFixedDelay传入的是负值,看下怎么处理这个时间的,
private void setNextRunTime() {
           long p = period;
           if (p > 0)
               time += p;
           else
               time = triggerTime(-p);
       }

/**
    * Returns the trigger time of a delayed action.
    */
   long triggerTime(long delay) {
       return now() +
           ((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
   }
  • 对于scheduleAtFixedRate,p>0,下次执行时间是上次执行时间+p。 对于scheduleWithFixedDelay,p<0,下次执行时间为当前时间+p,即本次任务的执行结束时间+p
  • scheduleWithFixedDelay前一个任务完成之后,开始计时,到指定的delay之后,有机会执行,具体执行时间看调度
  • scheduleAtFixedRate, 从任务提交开始,计算时间,按照指定的间隔执行任务。
public void run() {
            boolean periodic = isPeriodic();
            if (!canRunInCurrentRunState(periodic))
                cancel(false);
            else if (!periodic)
                ScheduledFutureTask.super.run();
            else if (ScheduledFutureTask.super.runAndReset()) {
                setNextRunTime();
                reExecutePeriodic(outerTask);
            }
        }
  • 我们对于scheduleThreadPool的任务提交,提交之后,即加入队列中。然后和ThreadPoolExecutor一样,由worker不断的从队列中取任务出来执行。且执行完之后,再设置下一次的执行时间,扔到队列里。
  • 这里的队列是维护了一个最小堆,每次从堆顶拿要执行的任务。上面我们知道,worker从队列中取任务时,是调用队列的take方法。
public RunnableScheduledFuture<?> take() throws InterruptedException {
            final ReentrantLock lock = this.lock;
            lock.lockInterruptibly();
            try {
                for (;;) {
                    RunnableScheduledFuture<?> first = queue[0];
                    if (first == null)
                        available.await();
                    else {
                        long delay = first.getDelay(NANOSECONDS);
                        if (delay <= 0)
                            return finishPoll(first);
                        first = null; // don't retain ref while waiting
                        if (leader != null)
                            available.await();
                        else {
                            Thread thisThread = Thread.currentThread();
                            leader = thisThread;
                            try {
                                available.awaitNanos(delay);
                            } finally {
                                if (leader == thisThread)
                                    leader = null;
                            }
                        }
                    }
                }
            } finally {
                if (leader == null && queue[0] != null)
                    available.signal();
                lock.unlock();
            }
        }
  • 如果队列中没有任务,则等待,如果队首的任务已经到该执行的时间了,则直接返回,如果还没到delay的时间,则等待delay时间。
  • 这里leader是干啥的呢?leader用于记载当前哪个线程是第一个来取队首任务的线程,如果一个线程标记了,后面的线程就执行等待了,同时这个标记的线程只await delay的时间,这样,当次线程醒来的时候,直接可以取到队首的元素去执行了。如果不这样的话,加入所有的线程都await(),后面大家还需要再竞争一次。这样通过leader机制,减少了一次竞争。
  • 那如果再等待的时候,又来了一个更快执行的任务呢,
public boolean offer(Runnable x) {
            if (x == null)
                throw new NullPointerException();
            RunnableScheduledFuture<?> e = (RunnableScheduledFuture<?>)x;
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                int i = size;
                if (i >= queue.length)
                    grow();
                size = i + 1;
                if (i == 0) {
                    queue[0] = e;
                    setIndex(e, 0);
                } else {
                    siftUp(i, e);
                }
                if (queue[0] == e) {
                    leader = null;
                    available.signal();
                }
            } finally {
                lock.unlock();
            }
            return true;
        }
  • 如果队列里通过offer又加入了一个新的task,且加在了队首,那这时候,会调用 available.signal();,唤醒所有再等待的线程,大家再重新竞争,重新获取队首任务。
posted @ 2021-07-18 17:21  Nooooone  阅读(83)  评论(0)    收藏  举报