Java多线程技术四——定时器

发布时间:2023年12月23日

1 定时器的使用

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

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

?

? ? ? ? 因为TimerTask是一个抽象类,所以计划执行的代码要放入Timer-Task的子类中。

2 schedule(TimerTask task,Date time)方法

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

2.1 执行任务的时间晚于当前时间

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

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

    }
}

? ? ? ? 10秒之后任务成功执行。任务虽然执行完成了,但进程还未销毁,呈红色状态,说明内部还有非守护线程正在执行。为什么会出现这个情况,请看下面的介绍。

2.2 线程TimerThread不销毁的原因

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

 public Timer() {
        this("Timer-" + serialNumber());
    }

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

 private final TimerThread thread = new TimerThread(queue);
 public Timer(String name, boolean isDaemon) {
        var threadReaper = new ThreadReaper(queue, thread);
        this.cleanup = CleanerFactory.cleaner().register(this, threadReaper);
        thread.setName(name);
        thread.setDaemon(isDaemon);
        thread.start();
    }

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

class TimerThread extends Thread {}

? ? ? ? 这个新启动的线程并不是守护线程,而且一直在运行。一直在运行的原因是新线程内部有一个死循环,TimerThread.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) {
            }
        }
    }

????????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() && new TashMaryBeScheduled条件进行判断。

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

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

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

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

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

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

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

????????

? ? ? ? 运行了18秒后,Timer-0的线程TimerThread被销毁了,但是进程还是呈红色状态,这是因为main线程一直在执行Thread.sleep(Interger.MAX_VALUE)?代码。

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

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

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

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

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

public class Run1 {
    public static void main(String[] args) {
        long nowTime = System.currentTimeMillis();
        System.out.println("当前时间是:" + Utils.data(nowTime));
        long sch1 = (nowTime + 5000);
        long sch2 = (nowTime + 8000);
        System.out.println("计划执行时间1是: "+ Utils.data(sch1));
        System.out.println("计划执行时间2是: "+ Utils.data(sch2));
        MyTask task1 = new MyTask();
        MyTask task2 = new MyTask();
        Timer timer = new Timer();
        timer.schedule(task1,new Date(sch1));
        timer.schedule(task2,new Date(sch2));
    }
}

文章来源:https://blog.csdn.net/geminigoth/article/details/135051283
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。