⭐️前面的话⭐️

本篇文章将介绍Java多线程案例,定时器,定时器就像闹钟一样,等到了指定的时间,闹钟就会发出响声来提醒您,而定时器会执行指定的任务。

🍎1.定时器概述

🍏1.1认识定时器

java中的定时器,也可以叫做任务定时执行器,顾名思义,就是等到了指定的时间,就去做指定的事情,就像你每周六或周日都要去力扣参加周赛一样。

所以你如果想要使用定时器,你需要交代时间和对应的任务才行,java标准也提供了带有定时器功能的类Timer

🍏1.2Timer类的使用

在java1.8中,Timer给出了四个构造方法,这些构造方法可以去指定线程的名字和是否将定时器内部的线程指定为守护线程。

好了,又出现了一个新概念,这个守护线程是什么鬼?
其实在java中有两种线程,一种是用户线程,另外一种是守护线程。用户线程就是普通的线程,守护线程顾名思义就是守护用户线程的线程,可以说就是用户线程的保姆,守护线程与JVM“共存亡”, 只要存在一个用户线程,程序中所有的守护线程都不会停止工作,直到最后一个用户线程执行完毕,守护线程才会停止工作。守护线程最典型的应用就是 GC (垃圾回收器),它就是一个非常称职的守护者。

🍊构造方法:

序号 构造方法 说明
1 public Timer() 无参数构造方法,默认定时器关联的线程不是守护线程,线程名字也是默认值
2 public Timer(boolean isDaemon) 指定定时器中关联的线程是否为守护线程,如果是,参数为true
3 public Timer(String name) 指定定时器关联线程名称,线程类型默认为非守护线程
4 public Timer(String name, boolean isDaemon) 指定定时器关联线程名和线程类型

Timer类构造时内部也会创建线程,如果不指定,定时器对象内部的线程(为了简化,就称为关联线程吧)的类型是用户线程,而不是守护线程。

🍊核心方法:

序号 方法 说明
1 public void schedule(TimerTask task, long delay) 指定任务,延迟多久执行该任务
2 public void schedule(TimerTask task, Date time) 指定任务,指定任务的执行时间
3 public void schedule(TimerTask task, long delay, long period) 连续执行指定任务,延迟时间,连续执行任务的时间间隔,毫秒为单位
4 public void schedule(TimerTask task, Date firstTime, long period) 连续执行指定任务,第一次任务的执行时间,连续执行任务的时间间隔
5 public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) 与方法4作用相同
6 public void scheduleAtFixedRate(TimerTask task, long delay, long period) 与方法3作用相同
7 public void cancel() 终止定时器所有任务,终止执行的任务不受影响

🍊使用演示:

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.PriorityBlockingQueue;

public class TimeProgram {
    public static void main(String[] args) throws InterruptedException {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("执行延后2s执行的任务!");
            }
        }, 2000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("执行延后5s执行的任务!");
            }
        }, 5000);

        //每秒输出一个mian
        for (int i = 0; i < 5; i  ) {
            System.out.println("main");
            Thread.sleep(1000);
        }
    }
}

🍊运行结果:

TimerTask类就是专门描述定时器任务的一个抽象类,它实现了Runnable接口。

public abstract class TimerTask implements Runnable    //jdk源码

下面我们简单实现一下定时器,我们就不用TimerTask了,我们直接使用Runnable,因为TimerTask实现了Runnable接口,所以后面测试我们自己所写的schedule方法时,也可以传入TimerTask类型的引用,既然是简单地实现,那就不实现连续执行的功能了。

🍎2.定时器的简单实现

首先,我们需要建造一个类,来描述定时器的任务,可以使用Runnable加上一个任务执行的时间戳就可以了。
🍊具体清单:
一个构造方法,用来指定任务和延迟执行时间。
两个获取方法,用来给外部对象获取该对象的任务和执行时间。
实现比较器,用于定时器任务对象的组织,毕竟,每次需要执行时间最早的任务,需要用到基于小根堆实现的优先队列,不,还需要考虑多线程的情况,还是使用优先级阻塞队列吧。

//我的任务
class MyTask implements Comparable<MyTask> {
    //接收具体任务
    private Runnable runnable;
    //执行时的时间戳
    private long time;

    //构造方法
    public MyTask(Runnable runnable, int delay) {
        this.runnable = runnable;
        this.time = System.currentTimeMillis()   delay;
    }

    //执行任务
    public void run() {
        this.runnable.run();
    }
    //获取执行时间
    public long getTime() {
        return this.time;
    }

    //实现comparable接口,方便创建优先级阻塞队列
    @Override
    public int compareTo(MyTask o) {
        return (int) (this.time - o.time);
    }
}

接下来就要实现定时器类了,首先我们需要一个数据结构来组织定时器任务,并且每次都能将时间最早的任务找到并执行,那么这个数据结构非小根堆莫属了,也就是优先级队列,注意对自定义类使用优先级队列时,一定要实现比较器。

    //每次执行任务时,需要优先执行时间在前的任务,即每次执行任务要选择时间戳最小的任务,在多线程情况中优先级阻塞队列是最佳选择
    private static final PriorityBlockingQueue<MyTask> priorityBlockingQueue = new PriorityBlockingQueue<>();

然后,需要一个方法将任务安排在优先级阻塞队列中,最后在构造定时器对象的时候从优先级阻塞队列中取任务并在指定的时间执行。

按照上图的逻辑,我们自己实现的定时器类需要有一个线程专门去执行任务,执行任务过程中可能会遇到执行时间还没有到的情况,那么线程必须得等待,线程等待的方法有两种,一种是wait另一种是sleep,这个案例我们推荐前者,因为sleep方法不能中途唤醒,这个案例是有可能需要中途唤醒的,那就是有新任务插入时,需要重新去优先级阻塞队列拿任务重复上述操作,这个唤醒操作可以使用notify方法实现,所以需要用到wait/notify组合拳,既然需要使用wait/notify那么就得有锁,所以我们可以使用一个专门的锁对象来加锁。

🍊实现代码:

//我的定时类 用来管理任务
class MyTimer {
    //专门对锁对象
    private final Object locker = new Object();
    //每次执行任务时,需要优先执行时间在前的任务,即每次执行任务要选择时间戳最小的任务,在多线程情况中优先级阻塞队列是最佳选择
    private static final PriorityBlockingQueue<MyTask> priorityBlockingQueue = new PriorityBlockingQueue<>();

    //安排任务
    public void schedule(Runnable runnable, int delay) {
        //将任务放入小根堆中
        MyTask task = new MyTask(runnable, delay);
        priorityBlockingQueue.put(task);
        //每次当新任务加载到阻塞队列时,需要中途唤醒线程,因为新进来的任务可能是最早需要执行的
        synchronized (locker) {
            locker.notify();
        }
    }
    public MyTimer() {
        Thread thread = new Thread(() -> {
            while (true) {
                try {
                    //加载任务,确定执行时机
                    MyTask myTask = priorityBlockingQueue.take();
                    long curTime = System.currentTimeMillis();
                    //时间未到,将任务放回
                    if (curTime < myTask.getTime()) {
                        synchronized (locker) {
                            priorityBlockingQueue.put(myTask);
                            //放回任务后,不能立即就再次取该任务加载,需要设置一个再次加载的等待时间,建议使用wait带参数的方法
                            //因为wait方法可以使用notify进行中途唤醒,而sleep不能中途唤醒
                            int delay = (int)(myTask.getTime() - curTime);
                            locker.wait(delay);
                        }
                    } else {
                        System.out.println(Thread.currentThread().getName()   "线程收到任务,正在执行中!");
                        myTask.run();
                        System.out.println(Thread.currentThread().getName()   "线程执行任务完毕,正在等待新任务!");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //不要忘了启动线程
        thread.start();
    }
}

🍊上面是我们实现定时器的代码,我们来测试一下:

import java.util.TimerTask;
import java.util.concurrent.PriorityBlockingQueue;

public class TimeProgram {
    public static void main(String[] args) throws InterruptedException {
        MyTimer timer = new MyTimer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("执行延后2s执行的任务!");
            }
        }, 2000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("执行延后5s执行的任务!");
            }
        }, 5000);

        //每秒输出一个mian
        for (int i = 0; i < 5; i  ) {
            System.out.println("main");
            Thread.sleep(1000);
        }
    }
}

🍊执行结果:

好了,任务定时执行器你学会了吗?

到此这篇关于Java任务定时执行器案例的实现的文章就介绍到这了,更多相关Java任务定时执行器内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

Java任务定时执行器案例的实现的更多相关文章

  1. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  2. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  3. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  4. Java实现世界上最快的排序算法Timsort的示例代码

    Timsort 是一个混合、稳定的排序算法,简单来说就是归并排序和二分插入排序算法的混合体,号称世界上最好的排序算法。本文将详解Timsort算法是定义与实现,需要的可以参考一下

  5. Java日期工具类的封装详解

    在日常的开发中,我们难免会对日期格式化,对日期进行计算,对日期进行校验,为了避免重复写这些琐碎的逻辑,我这里封装了一个日期工具类,方便以后使用,直接复制代码到项目中即可使用,需要的可以参考一下

  6. Java设计模式之模板方法模式Template Method Pattern详解

    在我们实际开发中,如果一个方法极其复杂时,如果我们将所有的逻辑写在一个方法中,那维护起来就很困难,要替换某些步骤时都要重新写,这样代码的扩展性就很差,当遇到这种情况就要考虑今天的主角——模板方法模式

  7. Java 中 Class Path 和 Package的使用详解

    这篇文章主要介绍了Java 中 Class Path和Package的使用详解,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下

  8. java SpringBoot 分布式事务的解决方案(JTA+Atomic+多数据源)

    这篇文章主要介绍了java SpringBoot 分布式事务的解决方案(JTA+Atomic+多数据源),文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的小伙伴可以参考一下

  9. Java一维数组和二维数组元素默认初始化值的判断方式

    这篇文章主要介绍了Java一维数组和二维数组元素默认初始化值的判断方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  10. java实现emqx设备上下线监听详解

    这篇文章主要为大家介绍了java实现emqx设备上下线监听详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部