定时器的使用

news2024/11/15 21:44:33

目录

前言

正文 

1.方法 schedule(TimerTask task, Date time) 的测试

(1)执行任务的时间晚于当前时间(在未来执行)的效果

(2)线程TimerThread不销毁的原因 

(3)使用 public void cancel() 方法实现 TimerThread 线程销毁

(4)执行任务的时间早于当前时间(立刻运行)的效果

(5)在定时器中执行多个 TimerTask 任务 

(6)延时执行 TimerTask 的测试

2.方法 schedule(TimerTask task, long delay, long period)

(1)执行任务的时间晚于当前时间(在未来执行)的效果 

(2)执行任务的时间早于当前时间(立即执行)的效果

(3)延迟执行 TimerTask 的测试 

(4)TimerTask 类中的 cancel() 方法

(5)类 Timer 中的 cancel() 方法 

(6)间隔执行任务的算法

(7)Timer 类中的 cancel() 方法的注意事项 

3.方法 schedule(TimerTask task,long delay) 的测试

4.方法 schedule(TimerTask task,long delay,long period) 的测试。

5.方法 public void scheduleAtFixedRate(TimerTask task, Date firstTime,long period)的测试

(1)测试 schedule 方法任务不延时(Date 类型)

(2)测试 schedule 方法任务不延时(Long 类型)

(3)测试 schedule 方法任务延时(Date 类型)

(4)测试 schedule 方法任务被延时(Long 类型) 

(5)测试 scheduleAtFixedRate 方法任务不延时(Date 类型)

(6)测试 scheduleAtFixedRate 方法任务不延时(Long 类型) 

(7)测试 scheduleAtFixedRate 方法任务延时(Date 类型)

(8)测试 scheduleAtFixedRate 方法任务延时(Long 类型) 

(9)验证 schedule 方法不具有追赶特性 

(10)验证 scheduleAtFixedRate 方法具有追赶性 


 

前言

定时/计划功能在移动开发领域使用较多,比如Android技术,定时功能在Java种主要通过Timer类实现,因为它在内部还是使用多线程的方式进行处理,所以和线程技术还是有非常大的关联。


正文 

 在 JDK 库中 Timer 类主要负责计划任务的功能 ,也就是在指定的时间开始执行某一个任务,Timer类的方法列表如图:

Timer类的主要作用就是设置计划任务,封装任务的类却是 TimerTask,该类的结构图:

 实现了Runnable接口。

因为TaskTimer是一个抽象类,所以执行任务的代码要放入Timer-Task的子类中。

1.方法 schedule(TimerTask task, Date time) 的测试

该方法的作用是在指定日期的执行一次某一任务。

(1)执行任务的时间晚于当前时间(在未来执行)的效果

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class schedule {
    static class MyTask extends TimerTask{
        @Override
        public void run() {
            System.out.println("任务执行了,时间为:"+System.currentTimeMillis());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        long nowTime = System.currentTimeMillis();
        System.out.println("当前时间为:"+nowTime);
        long scheduleTime = (nowTime+10000L);
        System.out.println("计划时间为:"+scheduleTime);
        MyTask task = new MyTask();
        Timer timer = new Timer();
        Thread.sleep(1000);
        timer.schedule(task,new Date(scheduleTime));
        Thread.sleep(Integer.MAX_VALUE);
    }


}

运行后的效果如图:

10s 之后任务成功执行。任务虽然执行完了,但线程还未销毁,呈红色状态,说明内部还有非守护线程正在执行,从 jvisualvm.exe工具中也得到了证实,程序运行 10s之后,名为 Timer-0 的线程还在运行,效果如图:

下面将介绍这种情况。

(2)线程TimerThread不销毁的原因 

进程不销毁的原因是在创建 Timer 类时启动了一个新的非守护进程,JDK 源代码如下:

    /**
     * Creates a new timer.  The associated thread does <i>not</i>
     * {@linkplain Thread#setDaemon run as a daemon}.
     */
    public Timer() {
        this("Timer-" + serialNumber());
    }

此构造方法调用的是如下构造方法。

private final TimerThread thread = new TimerThread(queue);
   public Timer(String name) {
        thread.setName(name);
        thread.start();
    }

查看构造方法可知,创建 一个Timer类时,在内部就启动了 1 个新的线程,用新启动的这个线程去执行计划任务,TimerThread是线程类,源代码如下:

/**
 * This "helper class" implements the timer's task execution thread, which
 * waits for tasks on the timer queue, executions them when they fire,
 * reschedules repeating tasks, and removes cancelled tasks and spent
 * non-repeating tasks from the queue.
 */
class TimerThread extends Thread {

那么这个新启动的线程并不是守护线程了,而且一直在运行。一直在运行的原因是新线程内部有一个死循环,TimeThread.java 类中的 mainLoop() 方法代码如下。

private void mainLoop() {
        while (true) {
            try {
                TimerTask task;
                boolean taskFired;
                synchronized(queue) {
                    // Wait for queue to become non-empty
                    while (queue.isEmpty() && newTasksMayBeScheduled)
                        queue.wait();
                    if (queue.isEmpty())
                        break; // Queue is empty and will forever remain; die

                    // Queue nonempty; look at first evt and do the right thing
                    long currentTime, executionTime;
                    task = queue.getMin();
                    synchronized(task.lock) {
                        if (task.state == TimerTask.CANCELLED) {
                            queue.removeMin();
                            continue;  // No action required, poll queue again
                        }
                        currentTime = System.currentTimeMillis();
                        executionTime = task.nextExecutionTime;
                        if (taskFired = (executionTime<=currentTime)) {
                            if (task.period == 0) { // Non-repeating, remove
                                queue.removeMin();
                                task.state = TimerTask.EXECUTED;
                            } else { // Repeating task, reschedule
                                queue.rescheduleMin(
                                  task.period<0 ? currentTime   - task.period
                                                : executionTime + task.period);
                            }
                        }
                    }
                    if (!taskFired) // Task hasn't yet fired; wait
                        queue.wait(executionTime - currentTime);
                }
                if (taskFired)  // Task fired; run it, holding no locks
                    task.run();
            } catch(InterruptedException e) {
            }
        }
    }

private void mainLoop()方法内部使用 while(true) 死循环一直执行计划任务,并不退出 while(true)死循环,依据源代码的执行流程,除非是满足 if (queue.isEmpty()) 条件,才执行 break 退出 while(true)死循环,退出逻辑的核心源代码如下:

 while (queue.isEmpty() && newTasksMayBeScheduled)
                        queue.wait();
               if (queue.isEmpty())
                        break; 

上面的源代码逻辑如下:

(1)使用 while 循环对 queue.isEmpty() && newTasksMayBeScheduled 条件进行判断。

(2)当 && 两端都为 true 时,执行 wait() 方法使当前线程被暂停运行,等待被唤醒。

(3)唤醒的时机是执行了 public void schedule(TimeTask task,Date time)方法,说明要执行新的任务了。

(4)唤醒后 while 继续判断 queue.isEmpty() && newTaskMayBeScheduled 条件,如果有新的任务被安排,则 queue.isEmpty() 结果为 false,&&最终结果为是 false,会继续执行下面的 if 语句。

(5)if(queue.isEmpty()) 中的 queue.isEmpty() 结果为 true,说明队列为空,那么执行 break 语句退出 while(true) 死循环。

(6)执行 public void cancel() 方法会使布尔变量 newTaskMayBeScheduled 的值由 true 变成 false。

(7)如果不执行 public void cancel(),变量 newTaskMayBeScheduled 的值就不会是 false,一直呈死循环的状态,进程不销毁就是这个原因,public void cancel() 方法源代码如下。


    public void cancel() {
        synchronized(queue) {
            thread.newTasksMayBeScheduled = false;
            queue.clear();
            queue.notify();  // In case queue was already empty.
        }
    }

上面 7 步就是进程不消毁的原因,以及退出死循环 while(true) 的逻辑。 

(3)使用 public void cancel() 方法实现 TimerThread 线程销毁

Timer 类中 public void cancel() 方法的作用是终止此计时器,丢弃当前所有已安排的任务。这不会干扰当前正在执行的任务(如果存在)。一旦终止了计时器,那么它的执行线程也会终止,并且无法根据它安排更多的任务。注意,在此计算器调用的计时器任务的 run() 方法内调用此方法,就可以确保正在执行的任务是此计算器的最后一个任务。虽然可以重复调用此方法,但是第二次和后续调用无效。

根据上面的源代码分析可知,当队列中为空,并且 newTaskMayBeScheduled 值是 false 时,退出 while(true) 死循环,导致 TimerThread 线程结束运行并销毁。

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Test {
    static class MyTask extends TimerTask {
        @Override
        public void run() {
            System.out.println("任务执行了,时间为:"+System.currentTimeMillis());
        }
    }
    public static void main(String[] args) throws InterruptedException {
        long nowTime = System.currentTimeMillis();
        System.out.println(" 当前时间为:" + nowTime);
        long scheduleTime = (nowTime + 15000L);
        System.out.println(" 计划时间为:"+scheduleTime);
        MyTask task = new MyTask();
        Timer timer = new Timer();
        timer.schedule(task,new Date(scheduleTime));
        Thread.sleep(18000);
        timer.cancel();
        Thread.sleep(Integer.MAX_VALUE);
    }
}

程序运行18秒之后,从 jvisualvm.exe工具中可以看到名称为 Timer-0 的线程 TimerThread被销毁(在已完成的线程中)了,效果如图: 

虽然 TimerThread 线程销毁了,但进程还是呈红色状态,这时因为main 线程一直在执行 Thread.sleep(Integer.MAX_VALUE) 代码,控制台输出如下:

(4)执行任务的时间早于当前时间(立刻运行)的效果

如果执行任务的时间早于当前时间,则立即执行任务。

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Test {
    static class MyTask extends TimerTask {
        @Override
        public void run() {
            System.out.println("任务执行了,时间为:"+System.currentTimeMillis());
        }
    }
    public static void main(String[] args) throws InterruptedException {
        long nowTime = System.currentTimeMillis();
        System.out.println(" 当前时间为:" + nowTime);
        long scheduleTime = (nowTime - 5000);
        System.out.println(" 计划时间为:"+scheduleTime);
        MyTask task = new MyTask();
        Timer timer = new Timer();
        timer.schedule(task,new Date(scheduleTime));
    }
}

效果如图:

(5)在定时器中执行多个 TimerTask 任务 

可以在定时器中执行多个 TimerTask 任务。 

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Test {
    static class MyTask extends TimerTask {
        @Override
        public void run() {
            System.out.println("任务执行了,时间为:"+System.currentTimeMillis());
        }
    }
    public static void main(String[] args) throws InterruptedException {
        long nowTime = System.currentTimeMillis();
        System.out.println(" 当前时间为:" + nowTime);
        long scheduleTime1 = (nowTime + 5000);
        long scheduleTime2 = (nowTime + 8000);
        System.out.println(" 计划时间1为:"+scheduleTime1);
        System.out.println(" 计划时间2为:"+scheduleTime2);
        MyTask task1 = new MyTask();
        MyTask task2 = new MyTask();
        Timer timer = new Timer();
        timer.schedule(task1,new Date(scheduleTime1));
        timer.schedule(task2,new Date(scheduleTime2));
    }
}

程序运行结果如图:

(6)延时执行 TimerTask 的测试

因为TimerTask是以一个队列得到方式一个一个被顺序执行的,所以执行的时间有可能和预期的时间不一致。因为前面的任务有可能消耗的时间较长,所以后面的任务运行的时间也被延后,请看下面的示例:

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class taskLater {
    static class MyTaskA extends TimerTask {
        @Override
        public void run() {
            try {
                System.out.println("A begin timer=" + System.currentTimeMillis());
                Thread.sleep(20000);
                System.out.println("A   end Timer=" + System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    static class MyTaskB extends TimerTask{

        @Override
        public void run() {
            System.out.println("B begin timer="+System.currentTimeMillis());
            System.out.println("B   end timer="+System.currentTimeMillis());
        }
    }

    public static void main(String[] args) {
        long nowTime = System.currentTimeMillis();
        System.out.println("当前时间为:"+nowTime);
        long scheduleTime1 = nowTime;
        long scheduleTime2 = nowTime+5000;
        System.out.println("计划时间1为:"+scheduleTime1);
        System.out.println("计划时间2为:"+scheduleTime2);
        MyTaskA taskA = new MyTaskA();
        MyTaskB taskB = new MyTaskB();
        Timer timer = new Timer();
        timer.schedule(taskA,new Date(scheduleTime1));
        timer.schedule(taskB,new Date(scheduleTime2));
    }
}

程序运行结果如图:

在代码中,long scheduleTime2 = now + 5000 原计划是执行任务1 和任务 2 的运行间隔时间为 5 秒,由于 task1 执行任务需要 20 秒,因此 task1 的结束时间就是 task2 的开始时间,task2 不再以 5 秒作为参考, 而是以 20 秒作为参考,究其原理是创建了 1 个Timer 类导致创建了 1 个TimerThread 线程,1 个TimerThread 线程管理 1 个队列,在队列中得按顺序运行任务。

2.方法 schedule(TimerTask task, long delay, long period)

该方法的作用是在指定的日期之后按指定的间隔周期无限循环地执行某一任务。

(1)执行任务的时间晚于当前时间(在未来执行)的效果 


import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class timerTest2_period {
    static class MyTask extends TimerTask{
        @Override
        public void run() {
            System.out.println("任务执行了,时间为:"+System.currentTimeMillis());
        }
    }

    public static void main(String[] args) {
        long nowTime = System.currentTimeMillis();
        System.out.println("当前时间为:"+nowTime);
        long scheduleTime = (nowTime+10000);
        System.out.println("计划时间为:"+scheduleTime);
        MyTask task = new MyTask();
        Timer timer = new Timer();
        timer.schedule(task,new Date(scheduleTime),4000L);
    }

}

程序运行结果如图:

从运行结果来看,每隔 4 秒运行一次 TimerTask 任务,并且是无限期重复执行任务。

(2)执行任务的时间早于当前时间(立即执行)的效果

如果计划时间早于当前时间,则立即执行任务。

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Test2 {
    static class MyTask extends TimerTask {
        @Override
        public void run() {
            System.out.println("任务执行了,时间为:"+System.currentTimeMillis());
        }
    }
    public static void main(String[] args) throws InterruptedException {
        long nowTime = System.currentTimeMillis();
        System.out.println(" 当前时间为:" + nowTime);
        long scheduleTime = (nowTime - 5000);
        System.out.println(" 计划时间为:"+scheduleTime);
        MyTask task = new MyTask();
        Timer timer = new Timer();
        timer.schedule(task,new Date(scheduleTime),4000L);
    }
}

程序运行结果如图:

控制台打印的结果是程序运行后立即执行任务,并且每隔 4 秒打印一次。 

(3)延迟执行 TimerTask 的测试 

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class timerTest2_periodLater {
    static class MyTaskA extends TimerTask{

        @Override
        public void run() {
            try{
                System.out.println("A begin timer="+System.currentTimeMillis());
                Thread.sleep(5000);
                System.out.println("A           end timer="+System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        long nowTime = System.currentTimeMillis();
        System.out.println("当前时间为:  "+nowTime);
        System.out.println("    计划时间为:  "+nowTime);
        MyTaskA taskA = new MyTaskA();
        Timer timer = new Timer();
        timer.schedule(taskA,new Date(nowTime),3000);
    }
}

使用代码 timer.schedule(taskA,new Date(nowTime),3000);是让每个任务执行的间隔时间为 3 秒,而运行结果却是 5 秒,这是因为在任务中执行了 Thread.sleep(5000) ,程序运行结果如图所示:

(4)TimerTask 类中的 cancel() 方法

TimerTask 类中的 cancel() 方法的作用是将自身从任务队列中清楚,该方法的源代码如下。 

    public boolean cancel() {
        synchronized(lock) {
            boolean result = (state == SCHEDULED);
            state = CANCELLED;
            return result;
        }
    }

从方法得到代码可以分析出,执行 TimerTask 类中的 cancel() 方法时是将当前 TimerTask 任务得到状态改为 CANCELLED。

测试代码如下:

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class TimerTaskCancelMethod {
    static class MyTaskA extends TimerTask{

        @Override
        public void run() {
            System.out.println("A run timer="+System.currentTimeMillis());
            this.cancel();
            System.out.println("A 任务自己移除自己");
        }
    }
    static class MyTaskB extends TimerTask{

        @Override
        public void run() {
            System.out.println("B run timer="+System.currentTimeMillis());
        }
    }

    public static void main(String[] args) {
        long nowTime = System.currentTimeMillis();
        System.out.println("当前时间为:"+nowTime);
        System.out.println("计划时间为:"+nowTime);
        MyTaskA task1 = new MyTaskA();
        MyTaskB task2 = new MyTaskB();
        Timer timer = new Timer();
        timer.schedule(task1,new Date(nowTime),4000L);
        timer.schedule(task2,new Date(nowTime),4000L);
     }
}

运行结果如下:

TimerTask 类的 cancel() 方法是将自身从任务队列中移除,其他任务不受影响。

(5)类 Timer 中的 cancel() 方法 

和 TimerTask 类中的 cancel() 方法清除自身不同,Timer 类中 cancel() 方法的作用是将任务队列中全部的任务清空,源代码如下。

 public void cancel() {
        synchronized(queue) {
            thread.newTasksMayBeScheduled = false;
            queue.clear();
            queue.notify();  // In case queue was already empty.
        }
    }

创建新的测试用例:

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class TimerCancelMethod {
    static class MyTaskA extends TimerTask {

        @Override
        public void run() {
            System.out.println("A run timer="+System.currentTimeMillis());
        }
    }
    static class MyTaskB extends TimerTask{

        @Override
        public void run() {
            System.out.println("B run timer="+System.currentTimeMillis());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        long nowTime = System.currentTimeMillis();
        System.out.println("当前时间为:"+nowTime);
        System.out.println("计划时间为:"+nowTime);
        MyTaskA task1 = new MyTaskA();
        MyTaskB task2 = new MyTaskB();
        Timer timer = new Timer();
        timer.schedule(task1,new Date(nowTime),2000L);
        timer.schedule(task2,new Date(nowTime),2000L);
        Thread.sleep(10000);
        timer.cancel();         //全部任务都取消
    }
}

程序运行 10 秒后,进程销毁,控制台输出结果如图:

全部任务都被清楚,并且进程被销毁,按钮由红色变成灰色。 

(6)间隔执行任务的算法

当队列中有三个任务 A B C 时,这三个任务执行顺序的算法是每次将最后一个任务放入队列头,再执行队列头中任务的 run() 方法,算法效果如下:

1)起始顺序为 A B C。

2)C A B,将 C  放入 AB 之前。

3)  B C A,将 B 放入 C A之前。

创建新的任务代码如下:

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class NewAlgorithm {
    static class MyTaskA extends TimerTask {

        @Override
        public void run() {
            System.out.println("A run timer="+System.currentTimeMillis());
        }
    }
    static class MyTaskB extends TimerTask{

        @Override
        public void run() {
            System.out.println("B run timer="+System.currentTimeMillis());
        }
    }
    static class MyTaskC extends TimerTask{

        @Override
        public void run() {
            System.out.println("C run timer="+System.currentTimeMillis());
        }
    }

    public static void main(String[] args) {
        long nowTime = System.currentTimeMillis();
        System.out.println("当前时间为:"+nowTime);
        System.out.println("计划时间为:"+nowTime);
        MyTaskA taskA = new MyTaskA();
        MyTaskB taskB = new MyTaskB();
        MyTaskC taskC = new MyTaskC();
        Timer timer = new Timer();
        timer.schedule(taskA,new Date(nowTime),2000L);
        timer.schedule(taskB,new Date(nowTime),2000L);
        timer.schedule(taskC,new Date(nowTime),2000L);
    }
}

运行结果如图:

(7)Timer 类中的 cancel() 方法的注意事项 

Timer() 类中的 cancel() 方法有时不一定会停止计划任务,而是正常执行。

创建新的项目:

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class TimerCancelError {
    static class MyTaskA extends TimerTask {
        private int i;

        public MyTaskA(int i) {
            super();
            this.i = i;
        }

        @Override
        public void run() {
            System.out.println(" 第" + i + "次没有被cancel取消");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        int i = 0;
        long nowTime = System.currentTimeMillis();
        System.out.println("当前时间为:"+nowTime);
        System.out.println("计划时间为:"+nowTime);
        while(true){
            i++;
            Timer timer = new Timer();
            MyTaskA taskA = new MyTaskA(i);
            timer.schedule(taskA,new Date(nowTime));
            timer.cancel();
        }
    }
}

程序运行后代码截图:

因为 Timer 类中的 cancel() 方法有时并没有争抢到队列锁,所以让TimerTask 任务正常执行。 

3.方法 schedule(TimerTask task,long delay) 的测试

该方法的作用是以执行 schedule(TimerTask task,long delay) 方法当前的时间为参考时间,在此时间基础上延迟指定的毫秒数后执行一次 TImerTask 任务。

import java.util.Timer;
import java.util.TimerTask;

public class timerTest3 {
    static class MyTask extends TimerTask{

        @Override
        public void run() {
            System.out.println("运行了!时间为"+System.currentTimeMillis());
        }
    }

    public static void main(String[] args) {
        MyTask task = new MyTask();
        Timer timer = new Timer();
        System.out.println("当前时间为:"+System.currentTimeMillis());
        timer.schedule(task,7000L);
    }
}

程序运行结果如图: 

 任务被延迟 7 秒执行了。

4.方法 schedule(TimerTask task,long delay,long period) 的测试。

该方法的作用是以执行 schedule(TimerTask task,long delay,long period) 方法当前的时间为参考时间,在此时间的基础上延迟指定的毫秒数,再以某一间隔时间无限次数地执行某一任务。

import java.util.Timer;
import java.util.TimerTask;

public class timerTest3 {
    static class MyTask extends TimerTask{

        @Override
        public void run() {
            System.out.println("运行了!时间为"+System.currentTimeMillis());
        }
    }

    public static void main(String[] args) {
        MyTask task = new MyTask();
        Timer timer = new Timer();
        System.out.println("当前时间为:"+System.currentTimeMillis());
        timer.schedule(task,3000L,5000);
    }
}

程序运行结果如图:

凡是使用方法中带有 period 参数的,都是无限循环执行 TimerTask 中的任务。

5.方法 public void scheduleAtFixedRate(TimerTask task, Date firstTime,long period)的测试

schedule 方法和 scheduleAtFixedRate 方法的主要区别在于有没有追赶特性

(1)测试 schedule 方法任务不延时(Date 类型)

创建项目,测试代码如下:

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class timerTest5 {
    static class MyTask extends TimerTask {

        @Override
        public void run() {
            try {
                System.out.println("begin timer="+System.currentTimeMillis());
                Thread.sleep(1000);
                System.out.println("    end timer="+System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        MyTask task = new MyTask();
        long nowTime = System.currentTimeMillis();
        Timer timer = new Timer();
        timer.schedule(task,new Date(nowTime),3000);
    }
}

运行结果如图:

控制台打印结果证明,在不延时的情况下,如果执行任务的时间没有被延时,则下一次执行任务的开始时间是上一次任务的开始时间加上 period 时间。

所谓的 “不延时” 是指执行任务的时间小于 period 间隔时间。

(2)测试 schedule 方法任务不延时(Long 类型)

import java.util.Timer;
import java.util.TimerTask;

public class Test2 {
    static class MyTask extends TimerTask {

        @Override
        public void run() {
            try {
                System.out.println("begin timer="+System.currentTimeMillis());
                Thread.sleep(1000);
                System.out.println("    end timer="+System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        MyTask task = new MyTask();
        System.out.println(" 当前时间:"+System.currentTimeMillis());
        Timer timer = new Timer();
        timer.schedule(task,3000L,4000L);
    }
}

运行结果如图:

控制台打印的结果证明,在不延时的情况下,如果执行任务的时间没有被延时,则第一次执行任务的时间是任务快开始时间加上延时时间,接下来执行任务的时间是上一次任务的开始时间加上 period 时间。 

(3)测试 schedule 方法任务延时(Date 类型)

创建测试用例:

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class timerTest5 {
    static class MyTask extends TimerTask {

        @Override
        public void run() {
            try {
                System.out.println("begin timer="+System.currentTimeMillis());
                Thread.sleep(5000);
                System.out.println("    end timer="+System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        MyTask task = new MyTask();
        long nowTime = System.currentTimeMillis();
        Timer timer = new Timer();
        timer.schedule(task,new Date(nowTime),2000);
    }
}

运行结果如图:

从控制台打印结果来看,在延时的情况下,如果执行任务的时间被延时,那么下一次任务的执行时间参考的是上一次任务 "结束" 的时间。 

(4)测试 schedule 方法任务被延时(Long 类型) 

测试用例如下:

import java.util.Timer;
import java.util.TimerTask;

public class Test2 {
    static class MyTask extends TimerTask {

        @Override
        public void run() {
            try {
                System.out.println("begin timer="+System.currentTimeMillis());
                Thread.sleep(5000);
                System.out.println("    end timer="+System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        MyTask task = new MyTask();
        System.out.println(" 当前时间:"+System.currentTimeMillis());
        Timer timer = new Timer();
        timer.schedule(task,3000L,2000L);
    }
}

运行结果如图:

从控制台打印结果来看,在延时的情况下,如果执行任务的时间被延时,那么下一次任务的执行时间参考的是上一次任务 "结束" 的时间。 

(5)测试 scheduleAtFixedRate 方法任务不延时(Date 类型)

创建新的测试用例,本次测试使用 scheduleAtFixedRate 作为测试。 

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class timerTest5 {
    static class MyTask extends TimerTask {

        @Override
        public void run() {
            try {
                System.out.println("begin timer="+System.currentTimeMillis());
                Thread.sleep(1000);
                System.out.println("    end timer="+System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        MyTask task = new MyTask();
        long nowTime = System.currentTimeMillis();
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(task,new Date(nowTime),3000);
    }
}

控制台打印的结果证明,在不延时的情况下,如果执行任务的时间没有被延时,则下一次执行任务的时间是上一次任务的开始时间加上 period 时间。 

(6)测试 scheduleAtFixedRate 方法任务不延时(Long 类型) 

创建测试用例: 

import java.util.Timer;
import java.util.TimerTask;

public class Test2 {
    static class MyTask extends TimerTask {

        @Override
        public void run() {
            try {
                System.out.println("begin timer="+System.currentTimeMillis());
                Thread.sleep(1000);
                System.out.println("    end timer="+System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        MyTask task = new MyTask();
        System.out.println(" 当前时间:"+System.currentTimeMillis());
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(task,3000L,4000L);
    }
}

运行结果如图:

控制台打印的结果证明,在不延时的情况下,如果执行任务的时间没有被延时,则下一次执行任务的时间是上一次任务的开始时间加上 period 时间。  

(7)测试 scheduleAtFixedRate 方法任务延时(Date 类型)

创建测试用例:


import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class timerTest5 {
    static class MyTask extends TimerTask {

        @Override
        public void run() {
            try {
                System.out.println("begin timer="+System.currentTimeMillis());
                Thread.sleep(5000);
                System.out.println("    end timer="+System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        MyTask task = new MyTask();
        long nowTime = System.currentTimeMillis();
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(task,new Date(nowTime),2000);
    }
}

运行结果如图:

 

从控制台打印结果来看,在延时的情况下,如果执行任务的时间被延时,那么下一次任务的执行时间参考的是上一次任务 "结束" 的时间。  

(8)测试 scheduleAtFixedRate 方法任务延时(Long 类型) 

创建测试用例:

import java.util.Timer;
import java.util.TimerTask;

public class Test2 {
    static class MyTask extends TimerTask {

        @Override
        public void run() {
            try {
                System.out.println("begin timer="+System.currentTimeMillis());
                Thread.sleep(5000);
                System.out.println("    end timer="+System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        MyTask task = new MyTask();
        System.out.println(" 当前时间:"+System.currentTimeMillis());
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(task,3000L,2000L);
    }
}

运行结果如图:

 

从控制台打印结果来看,在延时的情况下,如果执行任务的时间被延时,那么下一次任务的执行时间参考的是上一次任务 "结束" 的时间。 

从上面 8 个运行结果来看,schedule 方法和 scheduleAtFixedRate 方法在运行效果上并没有非常明显的区别,那它们之间到底有什么区别呢,那就是追赶特性。

(9)验证 schedule 方法不具有追赶特性 

创建测试用例:

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Test9 {
    static class MyTask extends TimerTask{

        @Override
        public void run() {
            System.out.println("begin timer="+System.currentTimeMillis());
            System.out.println("    end timer="+System.currentTimeMillis());
        }
    }

    public static void main(String[] args) {
        MyTask task = new MyTask();
        long nowTime = System.currentTimeMillis();
        System.out.println("现在执行时间:"+nowTime);
        long runTime = nowTime - 20000;
        System.out.println("计划执行时间:"+runTime);
        Timer timer = new Timer();
        timer.schedule(task,new Date(runTime),2000);
    }
}

运行结果如图:

 

从运行结果上来看,“1700620549103” 到 ”1700620529103“ 的时间所对应的任务被取消掉,不执行了,这就是任务不追赶。

(10)验证 scheduleAtFixedRate 方法具有追赶性 

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Test9 {
    static class MyTask extends TimerTask{

        @Override
        public void run() {
            System.out.println("begin timer="+System.currentTimeMillis());
            System.out.println("    end timer="+System.currentTimeMillis());
        }
    }

    public static void main(String[] args) {
        MyTask task = new MyTask();
        long nowTime = System.currentTimeMillis();
        System.out.println("现在执行时间:"+nowTime);
        long runTime = nowTime - 20000;
        System.out.println("计划执行时间:"+runTime);
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(task,new Date(runTime),2000);
    }
}

运行结果如图:

11次(开始1次加上{20000/2000}能运行的10次总共11次打印任务时间 “1700621411899” 都是曾经流逝时间的任务追赶,也就是将曾经没有执行的任务追加进行执行,将 20 秒之内执行任务的次数打印完,再每间隔 2 秒执行一次任务。

将两个时间段内的时间所对应的任务被 “弥补” 地执行,也就是在指定时间段内的运行次数必须要运行完整,这就是任务追赶特性

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1237204.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java 下载地址

1 地址&#xff1a; Java Downloads | Oracle 2 往下翻(Java 8 和 Java 11) 3 往下翻 (JRE 下载)(JRE 8 为例) 4 跳转到登录(登录账号才可以下载)

Flink Operator 使用指南 之 Flink Operator安装

介绍 Flink Kubernetes Operator 充当控制平面来管理 Apache Flink 应用程序的完整部署生命周期。尽管 Flink 的Native Kubernetes 集成已经允许用户在运行的 Kubernetes(k8s) 集群上直接部署 Flink 应用程序,但自定义资源和Operator Pattern 也已成为 Kubernetes 原生部署体…

跑步耳机哪个牌子好?这五款跑步耳机闭眼入也不会错!

作为一个经常跑步运动的人&#xff0c;总感觉运动能够让人暂时远离城市的喧嚣&#xff0c;同时运动也是一种特别好的舒压方法。但跑步的时候如果没有音乐助燃&#xff0c;那是没有灵魂的&#xff0c;这也许就是现代年轻人的矫情吧&#xff0c;我在运动的时候经常会佩戴骨传导耳…

渗透测试高级技巧(二):对抗前端动态密钥与非对称加密防护

在前文的技术分享中&#xff0c;我们描述了验签和静态对称加密&#xff08;静态密钥 AES&#xff09;的常见场景&#xff0c;大家我相信遇到类似的加解密清醒&#xff0c;基本都可以通过热加载的基本使用获得破解前端加密解密的方法&#xff0c;达到一个比较好的测试状态。 在…

无法创建 8192 MB 的匿名分页文件: 系统资源不足,无法完成请求的服务。

好久没用VMware Workstation&#xff0c;今天突然要用&#xff0c;发现所有的虚机在启动的时候提示都提示&#xff1a; 无法创建 XXXX MB 的匿名分页文件&#xff1a;页面文件太小&#xff0c;无法完成操作。 未能分配主内存。 模块"MainMem"启动失败。 未能启动…

[计算机网络实验]头歌 实验二 以太网帧、IP报文分析

目录 第1关&#xff1a;Wireshark基本使用入门 【实验目的】 【实验环境】 【本地主机、平台虚拟机之间数据传递】 wireshark基本用法】 1、wireshark主界面 2、抓取分组操作 3、Wireshark窗口功能 4、筛选分组操作 【实验操作】 ​编辑 第2关&#xff1a;Ethernet帧…

排序算法--冒泡排序

实现逻辑 ① 比较相邻的元素。如果第一个比第二个大&#xff0c;就交换他们两个。 ②对每一对相邻元素作同样的工作&#xff0c;从开始第一对到结尾的最后一对。在这一点&#xff0c;最后的元素应该会是最大的数。 ③针对所有的元素重复以上的步骤&#xff0c;除了最后一个。 ④…

怎么判断list是否为null

List<Entity> baseMess new ArrayList<>(); baseMess motiveService.getBaseMessage(machine.get(i),preDate,nowDate); System.out.println("获取Size"baseMess.size()); baseMess.removeIf(Objects::isNull); System.out.println("获取Size"…

NV080D语音芯片:让智能快递柜取件更便利

随着互联网的普及和电子商务的迅速发展&#xff0c;网购消费已经成为了越来越多人的选择。这也催生了一个庞大的“网购一族”&#xff0c;他们购买的各种商品会通过快递公司送到家门口。然而&#xff0c;收取快递往往也伴随着一系列问题。比如&#xff0c;派送时间和收件人取件…

使用Echarts.js绘制中国地图

使用Echarts.js绘制中国地图 一、页面效果 二、功能描述 ​ 1、展示中国所有省份&#xff0c;包括南海诸岛&#xff0c;确保领土完整&#xff0c;中国领土神圣且不可侵犯。 ​ 2、每个省份根据对应数据的不同渲染不同的颜色&#xff0c;根据数据从小到大&#xff0c;对应底部…

maven pom引入依赖不报红,但是项目Dependencies中没有引入jar包

前言 小编我将用CSDN记录软件开发求学之路上亲身所得与所学的心得与知识&#xff0c;有兴趣的小伙伴可以关注一下&#xff01; 也许一个人独行&#xff0c;可以走的很快&#xff0c;但是一群人结伴而行&#xff0c;才能走的更远&#xff01;让我们在成长的道路上互相学习&…

ROS2对比ROS1的一些变化与优势(全新安装ROS2以及编译错误处理)《1》

1、概述 我们在前面介绍的ROS&#xff0c;都是ROS1的版本&#xff0c;近期对机器狗进行学习的时候&#xff0c;发现版本是ROS2了&#xff0c;也发现平时习惯的一些命令都有了变化&#xff0c;改变还是挺大的&#xff0c;不过熟悉之后还是很习惯ROS2的写法。 ROS2不是在ROS1的基…

基于SSM的公司仓库管理系统(有报告)。Javaee项目

演示视频&#xff1a; 基于SSM的公司仓库管理系统&#xff08;有报告&#xff09;。Javaee项目 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring SpringMvc …

Sulfo-CY5 DBCO的荧光特点、激发发射-星戈瑞

**Sulfo-CY5 DBCO是一种近红外荧光标记探针&#xff0c;具有以下荧光特点&#xff1a; 激发波长&#xff1a;**Sulfo-CY5 DBCO的激发波长位于近红外区域&#xff0c;通常在650-670纳米之间。近红外光在生物体内具有较好的组织穿透性&#xff0c;能够减少组织自发荧光的干扰&…

ArkTS声明式开发范式

装饰器 用来装饰类、结构体、方法以及变量&#xff0c;赋予其特殊的含义&#xff0c;如上述示例中 Entry 、 Component 、 State 都是装饰器。 Component 表示这是个自定义组件&#xff1b; Entry 则表示这是个入口组件&#xff1b; State 表示组件中的状态变量&#xff0c;…

MySql表中添加emoji表情

共五处需要修改。 语句执行修改&#xff1a; ALTER TABLE xxxxx CONVERT TO CHARACTER SET utf8mb4;

全志R128芯片RTOS调试指南

RTOS 调试指南 此文档介绍 FreeRTOS 系统方案支持的常用软件调试方法&#xff0c;帮助相关开发人员快速高效地进行软件调试&#xff0c;提高解决软件问题的效率。 栈回溯 栈回溯是指获取程序的调用链信息&#xff0c;通过栈回溯信息&#xff0c;能帮助开发者快速理清程序执行…

线性代数 - 几何原理

目录 序言向量的定义线性组合、张成空间与向量基线性变换和矩阵线性复合变换与矩阵乘法三维空间的线性变换行列式矩阵的秩和逆矩阵维度变换点乘叉乘基变换特征值和特征向量抽象向量空间 序言 欢迎阅读这篇关于线性代数的文章。在这里&#xff0c;我们将从一个全新的角度去探索线…

NX二次开发UF_CAM_PREF_ask_logical_value 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CAM_PREF_ask_logical_value Defined in: uf_cam_prefs.h int UF_CAM_PREF_ask_logical_value(UF_CAM_PREF_t pref, logical * value ) overview 概述 This function provides …