zoukankan      html  css  js  c++  java
  • Java ScheduledExecutorService源码分析

    Java 定时任务可以用Timer + TimerTask来做,或者使用ScheduledExecutorService,使用ScheduledExecutorService有两个好处:

    1. 如果任务执行时间过长,TimerTask会出现延迟执行的情况。比如,第一任务在1000ms执行了4000ms,第二个任务定时在2000ms开始执行。这里由于第一个任务要执行4000,所以第二个任务实际在5000ms开始执行。这是由于Timer是单线程,且顺序执行提交的任务

    2. 如果执行任务抛出异常,Timer是不会执行会后面的任务的

    使用ScheduledExecutorService可以避免上面两种情况,因为ScheduledExecutorService是线程池,有多个线程执行。

    下面是一个使用ScheduledExecutorService实现定时任务的Demo

    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.ScheduledThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
     * Created by gxf on 2017/6/26.
     */
    public class TestSchedule {
        public static void main(String[] args) {
            ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(5);
            Task task = new Task();
            scheduledExecutorService.scheduleAtFixedRate(task, -10, 1, TimeUnit.SECONDS);
        }
    }
    
    class Task implements Runnable{
        public void run(){
            System.out.println("do task...");
        }
    }

    这里每隔1秒,控制台会输出do task

    ScheduledExecutorService是一个接口

    public interface ScheduledExecutorService extends ExecutorService {

    其中一个实现是

    public class ScheduledThreadPoolExecutor
            extends ThreadPoolExecutor
            implements ScheduledExecutorService {

    在Demo中

    ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(5);

    跟进ScheduledThreadPoolExecutor

    /**
         * Creates a new {@code ScheduledThreadPoolExecutor} with the
         * given core pool size.
         *
         * @param corePoolSize the number of threads to keep in the pool, even
         *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
         * @throws IllegalArgumentException if {@code corePoolSize < 0}
         */
        public ScheduledThreadPoolExecutor(int corePoolSize) {
            super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
                  new DelayedWorkQueue());
        }

    这里Super调用

    public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue) {
            this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                 Executors.defaultThreadFactory(), defaultHandler);
        }

    设定了线程池的各个参数,核心线程数,最大线程数,任务队列等。但这里还有线程创建,有任务提交了,才会创建线程池

    继续Demo中

    Task task = new Task();

    这里只是创建了一个Runable对象

    继续Demo

    scheduledExecutorService.scheduleAtFixedRate(task, -10, 1, TimeUnit.SECONDS);

    这里已经把任务提交给线程池了,进入方法

    /**
         * @throws RejectedExecutionException {@inheritDoc}
         * @throws NullPointerException       {@inheritDoc}
         * @throws IllegalArgumentException   {@inheritDoc}
         */
        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;
        }

    前面都是一些条件检查和包装,看最后的delayedExecute(t)

    /**
         * Main execution method for delayed or periodic tasks.  If pool
         * is shut down, rejects the task. Otherwise adds task to queue
         * and starts a thread, if necessary, to run it.  (We cannot
         * prestart the thread to run the task because the task (probably)
         * shouldn't be run yet.)  If the pool is shut down while the task
         * is being added, cancel and remove it if required by state and
         * run-after-shutdown parameters.
         *
         * @param task the task
         */
        private void delayedExecute(RunnableScheduledFuture<?> task) {
            if (isShutdown())
                reject(task);
            else {
                super.getQueue().add(task);
                if (isShutdown() &&
                    !canRunInCurrentRunState(task.isPeriodic()) &&
                    remove(task))
                    task.cancel(false);
                else
                    ensurePrestart();
            }
        }

    ok,这里可以看到任务被添加到了一个队列里面。在看最后,ensurePrestart()

    /**
         * Same as prestartCoreThread except arranges that at least one
         * thread is started even if corePoolSize is 0.
         */
        void ensurePrestart() {
            int wc = workerCountOf(ctl.get());
            if (wc < corePoolSize)
                addWorker(null, true);
            else if (wc == 0)
                addWorker(null, false);
        }

    这里可以看出,如果线程池里面的线程数,小于核心线程数,会继续添加线程。进入addWork(null, true)

     1 private boolean addWorker(Runnable firstTask, boolean core) {
     2         retry:
     3         for (;;) {
     4             int c = ctl.get();
     5             int rs = runStateOf(c);
     6 
     7             // Check if queue empty only if necessary.
     8             if (rs >= SHUTDOWN &&
     9                 ! (rs == SHUTDOWN &&
    10                    firstTask == null &&
    11                    ! workQueue.isEmpty()))
    12                 return false;
    13 
    14             for (;;) {
    15                 int wc = workerCountOf(c);
    16                 if (wc >= CAPACITY ||
    17                     wc >= (core ? corePoolSize : maximumPoolSize))
    18                     return false;
    19                 if (compareAndIncrementWorkerCount(c))
    20                     break retry;
    21                 c = ctl.get();  // Re-read ctl
    22                 if (runStateOf(c) != rs)
    23                     continue retry;
    24                 // else CAS failed due to workerCount change; retry inner loop
    25             }
    26         }
    27 
    28         boolean workerStarted = false;
    29         boolean workerAdded = false;
    30         Worker w = null;
    31         try {
    32             w = new Worker(firstTask);
    33             final Thread t = w.thread;
    34             if (t != null) {
    35                 final ReentrantLock mainLock = this.mainLock;
    36                 mainLock.lock();
    37                 try {
    38                     // Recheck while holding lock.
    39                     // Back out on ThreadFactory failure or if
    40                     // shut down before lock acquired.
    41                     int rs = runStateOf(ctl.get());
    42 
    43                     if (rs < SHUTDOWN ||
    44                         (rs == SHUTDOWN && firstTask == null)) {
    45                         if (t.isAlive()) // precheck that t is startable
    46                             throw new IllegalThreadStateException();
    47                         workers.add(w);
    48                         int s = workers.size();
    49                         if (s > largestPoolSize)
    50                             largestPoolSize = s;
    51                         workerAdded = true;
    52                     }
    53                 } finally {
    54                     mainLock.unlock();
    55                 }
    56                 if (workerAdded) {
    57                     t.start();
    58                     workerStarted = true;
    59                 }
    60             }
    61         } finally {
    62             if (! workerStarted)
    63                 addWorkerFailed(w);
    64         }
    65         return workerStarted;
    66     }

    第32行和33行可以看出,任务已经提交给Work类了,第57行,t.start()这里启动线程,执行提交的任务

    到这里,提交的任务已经开始执行了。这里,我们在看一下Worker这个包装类

    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;

    可以看到有Thread, task,这里,其实就是把task指向要执行的任务,thread作为作为线程执行任务。

    在ThreadPoolExecutor中

    private final HashSet<Worker> workers = new HashSet<Worker>();

    这个保存我们生成的线程,有了这个就不用重新创建和销毁线程了

    我们在看一下Worker的run()方法

    /** Delegates main run loop to outer runWorker  */
            public void run() {
                runWorker(this);
            }

    继续跟进

     1 final void runWorker(Worker w) {
     2         Thread wt = Thread.currentThread();
     3         Runnable task = w.firstTask;
     4         w.firstTask = null;
     5         w.unlock(); // allow interrupts
     6         boolean completedAbruptly = true;
     7         try {
     8             while (task != null || (task = getTask()) != null) {
     9                 w.lock();
    10                 // If pool is stopping, ensure thread is interrupted;
    11                 // if not, ensure thread is not interrupted.  This
    12                 // requires a recheck in second case to deal with
    13                 // shutdownNow race while clearing interrupt
    14                 if ((runStateAtLeast(ctl.get(), STOP) ||
    15                      (Thread.interrupted() &&
    16                       runStateAtLeast(ctl.get(), STOP))) &&
    17                     !wt.isInterrupted())
    18                     wt.interrupt();
    19                 try {
    20                     beforeExecute(wt, task);
    21                     Throwable thrown = null;
    22                     try {
    23                         task.run();
    24                     } catch (RuntimeException x) {
    25                         thrown = x; throw x;
    26                     } catch (Error x) {
    27                         thrown = x; throw x;
    28                     } catch (Throwable x) {
    29                         thrown = x; throw new Error(x);
    30                     } finally {
    31                         afterExecute(task, thrown);
    32                     }
    33                 } finally {
    34                     task = null;
    35                     w.completedTasks++;
    36                     w.unlock();
    37                 }
    38             }
    39             completedAbruptly = false;
    40         } finally {
    41             processWorkerExit(w, completedAbruptly);
    42         }
    43     }

    第8行getTask()取要执行的任务,第23行执行任务

    继续跟进第8行getTask()

     1 private Runnable getTask() {
     2         boolean timedOut = false; // Did the last poll() time out?
     3 
     4         for (;;) {
     5             int c = ctl.get();
     6             int rs = runStateOf(c);
     7 
     8             // Check if queue empty only if necessary.
     9             if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
    10                 decrementWorkerCount();
    11                 return null;
    12             }
    13 
    14             int wc = workerCountOf(c);
    15 
    16             // Are workers subject to culling?
    17             boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
    18 
    19             if ((wc > maximumPoolSize || (timed && timedOut))
    20                 && (wc > 1 || workQueue.isEmpty())) {
    21                 if (compareAndDecrementWorkerCount(c))
    22                     return null;
    23                 continue;
    24             }
    25 
    26             try {
    27                 Runnable r = timed ?
    28                     workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
    29                     workQueue.take();
    30                 if (r != null)
    31                     return r;
    32                 timedOut = true;
    33             } catch (InterruptedException retry) {
    34                 timedOut = false;
    35             }
    36         }
    37     }

    这里主要看第27-29,前面我们说过,任务是放到一个队列里面的。其实,是个阻塞队列,在队列为空的时候取,会阻塞线程。这里就用到了这个功能,在29行workQueue.take()如果没有任务,线程就会阻塞。有任务就会取任务进行执行。

    简单点:线程池就是一个Set对象 + BlockingQueue对象, Workers + BlockingQueue

  • 相关阅读:
    吃推荐3个最近去了的好地方
    30岁生日
    今天开始试用Briglow Hair Cream
    WPF中如何在文本外面加虚线外框
    对账算法改进
    如何退出正在Sleep的线程
    关于framework4.5的相关介绍
    恐怖的报警邮件
    对帐引擎2个月后的监控数据
    wcf rest服务启用gzip压缩
  • 原文地址:https://www.cnblogs.com/luckygxf/p/7082237.html
Copyright © 2011-2022 走看看