http://www.cnblogs.com/sunxucool/archive/2013/06/21/3148141.html
ScheduledThreadPoolExecutor除了具有ThreadPoolExecutor的所有功能外,还可以延迟执行任务或者周期性的执 行某个任务。scheduleWithFixedDelay和scheduleAtFixedRate就是用来完成这个功能的。平常使用 scheduleAtFixedRate这个方法时并没有多想,但是这几天在实现一个功能的时候,需要考虑scheduleAtFixedRate所执行 的task是否会影响任务的周期性,比如scheduleAtFixedRate(command,5,10,TimeUnit.SECONDS),那么 这个command的执行会不会影响这个10秒的周期性。因此特意仔细看了下ScheduledThreadPoolExecutor的源代码,这里记录 一下,以便以后查看。
scheduleAtFixedRate有两个时间参数,initialDelay和period,对应该方法的两个主要功能,即延迟运行任务和周期性执行任务。
- 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();
- RunnableScheduledFuture<?> t = decorateTask(command,
- new ScheduledFutureTask<Object>(command,
- null,
- triggerTime(initialDelay, unit),
- unit.toNanos(period)));
- delayedExecute(t);
- return t;
- }
- /**
- * Specialized variant of ThreadPoolExecutor.execute for delayed tasks.
- */
- private void delayedExecute(Runnable command) {
- if (isShutdown()) {
- reject(command);
- return;
- }
- // Prestart a thread if necessary. We cannot prestart it
- // running the task because the task (probably) shouldn't be
- // run yet, so thread will just idle until delay elapses.
- if (getPoolSize() < getCorePoolSize())
- prestartCoreThread();
- super.getQueue().add(command);
- }
首先创建一个ScheduledFutureTask,然后通过delayedExecute执行这个task。在delayedExecute中,首先 预先启动一个线程,这里要注意的是这个这里用来启动一个新线程的firstTask参数是null,所以新启动的线程是idle状态的,然后把这个 task加入到workQueue。ScheduledThreadPoolExecutor里的workQueue用的是 DelayedWorkQueue,这个DelayedWorkQueue就是实现delay的关键。DelayedWorkQueue内部使用的是 DelayQueue,DelayQueue实现task delay的关键就在于其Offer(E e)和Take.下面,通过分析这两个方法和结合ThreadPoolExecutor的运行原理来说明delay操作是如何实现的
- public boolean offer(E e) {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- E first = q.peek();
- q.offer(e);
- if (first == null || e.compareTo(first) < 0)
- available.signalAll();
- return true;
- } finally {
- lock.unlock();
- }
- }
- public E take() throws InterruptedException {
- final ReentrantLock lock = this.lock;
- lock.lockInterruptibly();
- try {
- for (;;) {
- E first = q.peek();
- if (first == null) {
- available.await();
- } else {
- long delay = first.getDelay(TimeUnit.NANOSECONDS);
- if (delay > 0) {
- long tl = available.awaitNanos(delay);
- } else {
- E x = q.poll();
- assert x != null;
- if (q.size() != 0)
- available.signalAll(); // wake up other takers
- return x;
- }
- }
- }
- } finally {
- lock.unlock();
- }
- }
ScheduledThreadPoolExecutor执行task是通过工作线程Work来承担的,Work的Run方法如下:
- public void run() {
- try {
- Runnable task = firstTask;
- firstTask = null;
- while (task != null || (task = getTask()) != null) {
- runTask(task);
- task = null;
- }
- } finally {
- workerDone(this);
- }
- }
因为前面在delayedExecute方法里面创建work线程的firstTask参数为null,所以就通过getTask去从workQueue 里面获取task,getTask在正常情况下(即线程池没有关闭,线程数量没有超过corePoolSize等)是通过 workQueue.take()从workQueue里获取任务。根据上面的贴出来的take方法的代码,如果queue是空的,则take方法会阻塞 住,直到有新task被add进来。而在上面的delayedExecute方法的最后,会把创建的scheduledFutureTask加入到 workQueue,这样take方法中的available.await()就被唤醒;在take方法里面,如果workQueue不为空,则执行 task.getDelay()方法获取task的delay
- public long getDelay(TimeUnit unit) {
- return unit.convert(time - now(), TimeUnit.NANOSECONDS);
- }
这里的time是通过两个方法把initialDelay变成一个triggerTime
- /**
- * Returns the trigger time of a delayed action.
- */
- private long triggerTime(long delay, TimeUnit unit) {
- return triggerTime(unit.toNanos((delay < 0) ? 0 : delay));
- }
- /**
- * Returns the trigger time of a delayed action.
- */
- long triggerTime(long delay) {
- return now() +
- ((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
- }
注意看这个方法,这里返回的delay不是固定不变的,从task被放入workQueue起,不同的时间调用getDelay方法会得出不同的 delay。如果放入workQueue的task的initialDelay是5秒,那么根据take方法的代码,如果在放入workQueue5秒 后,就可以从delayQueue中拿到5秒前put进去的task,这样就实现了delay的功能。
在本文的最前面提到scheduleAtFixedRate能够周期性地执行一项任务,那么这个是如何实现的呢?在 scheduleAtFixedRate方法里创建了一个ScheduledFutureTask,这个ScheduledFutureTask包装了 command,最后周期性执行的是ScheduledFutureTask的run方法。
- private void runPeriodic() {
- boolean ok = ScheduledFutureTask.super.runAndReset();
- boolean down = isShutdown();
- // Reschedule if not cancelled and not shutdown or policy allows
- if (ok && (!down ||
- (getContinueExistingPeriodicTasksAfterShutdownPolicy() &&
- !isStopped()))) {
- long p = period;
- if (p > 0)
- time += p;
- else
- time = triggerTime(-p);
- ScheduledThreadPoolExecutor.super.getQueue().add(this);
- }
- // This might have been the final executed delayed
- // task. Wake up threads to check.
- else if (down)
- interruptIdleWorkers();
- }
- /**
- * Overrides FutureTask version so as to reset/requeue if periodic.
- */
- public void run() {
- if (isPeriodic())
- runPeriodic();
- else
- ScheduledFutureTask.super.run();
- }
由上面的代码可以看出,scheduleAtFixedRate(command,5,10,TimeUnit.SECONDS)这个方法的周期性会受 command的影响,如果command方法的执行时间是10秒,那么执行command的周期其实是20秒,即 scheduleAtFixedRate这个方法要等一个完整的command方法执行完成后才继续周期性地执行command方法,其实这样的设计也是 符合常理的。
相关推荐
Java 中 Timer 的 schedule 和 scheduleAtFixedRate 方法区别详解 Java 中的 Timer 类提供了两个常用的方法:schedule 和 scheduleAtFixedRate,用于安排和执行定时任务。虽然这两个方法看起来很相似,但是它们之间...
本篇文章将深入探讨 `Timer` 类的两个核心方法:`schedule` 和 `scheduleAtFixedRate`,以及它们在实际开发中的应用。 首先,`schedule` 方法有两种重载形式,一种是安排一个 `TimerTask` 在特定的延迟后执行,另一...
该接口提供了两个方法,`schedule`和`scheduleAtFixedRate`,它们都能用于安排任务的执行,但具体的行为有所不同。本文将深入探讨这两个方法的异同。 1. **schedule方法** `schedule`方法用于一次性延迟执行任务,...
2. 使用 ScheduledThreadPoolExecutor 的 scheduleAtFixedRate 方法提交任务,指定超时时间后执行任务。 3. 在请求进入熔断器时,提交任务到线程池中等待执行。 4. 如果接口在超时时间内没有返回,任务将被触发,...
Java线程池 Java线程池是Java语言中的一种高级多线程处理机制,用于避免重复创建和销毁线程而导致额外的性能开销。Java线程池功能可以实现...在上面的示例代码中,我们使用了scheduleAtFixedRate方法来执行定时任务。
在`ScheduledThreadPoolExecutor`中,任务的定时执行是通过`schedule()`、`scheduleAtFixedRate()`和`scheduleWithFixedDelay()`方法实现的。这些方法允许用户设定首次执行的时间延迟,以及后续执行的间隔。它们会...
`ScheduledThreadPoolExecutor` 提供了`scheduleAtFixedRate`方法来实现定时执行任务。这个方法接受四个参数: - `command`: 需要执行的任务,可以自定义实现`Runnable`接口。 - `initialDelay`: 第一次执行任务前...
然后,我们可以通过ScheduledExecutorService的scheduleWithFixedDelay()或scheduleAtFixedRate()方法来安排任务。例如,使用Runnable来实现定时任务: ```java ScheduledFuture<?> future = ...
需要注意的是,schedule 方法和 scheduleAtFixedRate 方法的区别。在 schedule 方法中,如果指定的开始执行时间在当前系统运行时间之前,schedule 方法不会把过去的时间算上。例如,在上面的代码中,我们使用 ...
schedule方法计算下一次执行的时间是通过当前时间 + 时间片,而scheduleAtFixedRate方法计算下一次执行的时间是通过当前需要执行的时间 + 时间片。 六、Timer的内部结构 Timer类里包含一个任务队列和一个异步轮询...
ScheduledExecutorService提供了两种方法来实现定时任务的执行,分别是scheduleAtFixedRate和scheduleWithFixedDelay。 scheduleAtFixedRate方法 scheduleAtFixedRate方法用于实现定时任务的执行,例如,每隔3秒...
除了`schedule()`方法,还有`scheduleAtFixedRate()`方法,它在处理定时任务时会保持恒定的时间间隔,即使上一次执行超时也不会影响下一次执行的时间。这对于需要精确控制执行间隔的场景非常有用。 需要注意的是,`...
在上面的例子中,我们使用scheduleAtFixedRate方法来执行任务。在这个例子中,我们让任务在 1 秒后执行,并且每次间隔 2 秒钟。 java.util.TimerTask类 java.util.TimerTask类是Java Timer定时器的任务类,它可以...
我们可以通过`scheduleAtFixedRate`或`scheduleWithFixedDelay`方法来安排任务的定期执行。尽管其功能相对有限,但对于小型项目或者不需复杂调度的情况,ScheduledThreadPoolExecutor是一个很好的选择。 在...
Timer类提供了两种执行任务的模式,一种是schedule方法,另一种是scheduleAtFixedRate方法。 schedule方法可以在某个时间点或在某个固定的时间间隔内执行任务。例如, timer.schedule(new MyTask(), 1000, 2000),...
然后,使用`Timer`类的`schedule()`或`scheduleAtFixedRate()`方法设置任务的执行时间或周期。 除了`Timer`和`TimerTask`,Java 5引入了`java.util.concurrent`包,其中包含更强大的`ScheduledExecutorService`接口...
创建一个TimerTask实例,覆盖run()方法实现UI更新,然后使用Timer的scheduleAtFixedRate()方法设定每隔1秒执行一次。 ```java Timer timer = new Timer(); TimerTask task = new TimerTask() { @Override public ...
它提供了多种方法来执行任务,如 scheduleAtFixedRate、scheduleWithFixedDelay 等。ScheduledExecutorService 可以用来实现各种定时任务,如每天某个时间执行某个任务、每隔一段时间执行某个任务等。 ...
- 创建一个`Timer`实例,然后使用`scheduleAtFixedRate`方法设定倒计时的初始延迟(比如5分钟)和周期(比如1秒),这样每过1秒,`run`方法就会被调用一次,更新倒计时。 - 在`run`方法中,每次调用时减去1秒,...
创建一个`Timer`对象后,可以调用其`schedule`或`scheduleAtFixedRate`方法来安排任务执行。`Timer`是线程安全的,意味着多个线程可以共享一个`Timer`对象,而不会出现竞态条件。 2. **`java.util.TimerTask`类**:...