Java中的定时器Timer详解

目录
  • 总结

简单来说,定时器就相当于一个“闹钟”,给定时器设定一个任务,约定这个任务在xxx时间之后执行~

Timer类提供了一个核心接口,schedule(安排) 指定一个任务交给定时器,在一定时间之后再去执行这个任务~

如何实现定时器的效果~

  • Timer中要包含一个Task类,每个Task就表示一个具体的任务实例,Task里面包含一个时间戳(啥时候执行这个任务),还包含一个Runnable实例(用来表示任务具体是啥)。
  • Timer里面通过一个带优先级的阻塞队列,来组织如干个task。
  • 这里的优先级是按照时间的先后来排优先级,快要到时间的任务优先级更高。
  • 也就是 给 堆 加上 wait/notify ,让它能够带阻塞效果。
  • 标准库里提供这样的队列PriorityBlockingQueue
  • Timer 中还需要一个专门的线程,让这个线程不停的扫描队首元素,看看队首元素是不是已经可以执行了,如果可以执行了,就执行,反之继续在队列中等待。

具体如何用代码实现这样一个定时器Timer:

一般去设定时间的时候,传入的时间,都是一个时间间隔

例如:传入1000 ,就代表从当前开始过1000ms之后在执行;

而我这里为了后面代码方便判断,在这里记录一下绝对时间,这样this.time里就是一个标志的ms级时间戳了,后续只需要获取当前时间戳在和这里的time对比一下就好了。

Task 要放到一个优先级队列中,但是优先级队列里面是需要比较优先级的,所以可以让Task类实现Comparable接口,重写compareTo方法来进行比较。

这里希望时间较小的排在见面。

这里获取一下当前时间currTime,如果当前时间大于等于task里约定的时间,超过说明时间到,执行任务,反之没到,把取去的

任务再放回队列,继续等待。

这里还涉及到一个问题:

举个例子:假如你早上定了一个8.30的 闹钟,8点的时候你醒了,看了下时间,还没到,你就继续睡,

但是这里是while(true),就意味着每过一秒钟就要看一次闹钟,8.01看一次,8.02看一次,这样就会白白的浪费一些资源,这就出现了“盲等”,在等待任务时间的过程中,一直持有着CPU资源~

所以这里就需要优化一下:使用wait/notify机制,就可很好的改善盲等问题~

如果取出任务发现还没到时间,就wait,等待一定时间,这里使用的wait()的重载版本,wait()里写一个参数,达到等待时间,自动醒过来~ 此时就大大降低了扫描次数和成本,

这里的notify(),就是保证当线程中如果有线程在WAITING状态的线程,就需要显示的唤醒一下线程。

举个例子;

如果队首元素8.30在执行,等待30分钟,但是此时,可能突然插入一个任务,让你8.10的时候去干一件事,如果你8.30再去唤醒的话,8.10的任务就来不及了!

所以每次插入新任务的时候,都唤醒一下woeker线程,让worker线程重新获取一下队首元素,看看接下来的任务等待多少时间合适。

//简单定时器
public class TestTimer {
    //每个 Task 实例 就包含一个要执行的任务
    //Task 要放到一个优先级队列中,但是优先级队列里面是需要比较优先级的
    static class Task implements Comparable<Task>{
        //什么时候执行
        private long time;
        //执行什么任务
        private  Runnable command;

        public Task(Runnable command ,long time){
            this.command = command;
            this.time = System.currentTimeMillis()+time;
        }

        public void  run(){
        //执行Runable 里面的run方法
            command.run();
        }

        @Override
        public int compareTo(Task o) {
            return (int)(this.time - o.time);
        }
    }
    static class Timer{
        //先创建一个带优先级的阻塞队列
        private PriorityBlockingQueue<Task>  queue = new PriorityBlockingQueue<>();

        //用这个对象来完成线程之间的协调任务
        private Object meilbox = new Object();

        //schedule 方法就是把一个Task 放在Timer中
        public void schedule(Runnable command,long after){
            Task task = new Task(command,after);
            //将当前任务放入对列
            queue.put(task);
            //当worker 线程中包含wait机制的时候,在安排任务的时候就需要显示的唤醒一下
            synchronized (meilbox){
                meilbox.notify();
            }
        }
        //写一个构造方法,创建线程
        public Timer(){
            //创建一个线程,让这个线程去扫描队列的队首元素,看看能不能执行
            Thread worker = new Thread(){
                @Override
                public void run() {
                    //取出队首元素,判断这个元素能不能执行
                    while(true){
                        try {
                            Task task = queue.take();
                            long currTime = System.currentTimeMillis();
                            if(currTime >= task.time){
                                //时间到,执行任务
                                task.run();
                            }else{
                                //时间没到,继续等待
                                queue.put(task);
                                synchronized (meilbox){
                                    meilbox.wait(task.time-currTime);
                                }
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            worker.start();
        }
    }
}

测试:

public static void main(String[] args) {
        Timer timer = new Timer();
        Runnable command = new Runnable() {
            @Override
            public void run() {
                System.out.println("时间到了~");
               // timer.schedule(this,3000); 每隔3是就执行一次
            }
        };
        System.out.println("安排任务");
        timer.schedule(command,3000);
    }
}

安排任务后,等待3s就可以执行了

这里补充一下Timer原生类中的一些方法

  • schedule(TimerTask task, Date time)   在指定的日期执行一次TimerTask任务;如果日期time早于当前时间,则立刻执行。
  • schedule(TimerTask task, long delay, long period)   以当前时间为基准,延迟指定的毫秒后,再按指定的时间间隔地无限次数的执行TimerTask任务。
  • schedule(TimerTask task, Date firstTime, long period)   在指定的日期之后,按指定的时间间隔地无限次数的执行TimerTask任务。
  • scheduleAtFixedRate(TimerTask task, long delay, long period)   以当前时间为基准,延迟指定的毫秒后,再按指定的时间间隔周期性地无限次数的执行TimerTask任务。

总结

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • Java 中Timer和TimerTask 定时器和定时任务使用的例子

    这两个类使用起来非常方便,可以完成我们对定时器的绝大多数需求 Timer类是用来执行任务的类,它接受一个TimerTask做参数 Timer有两种执行任务的模式,最常用的是schedule,它可以以两种方式执行任务:1:在某个时间(Data),2:在某个固定的时间之后(int delay).这两种方式都可以指定任务执行的频率 TimerTest.Java: package com.cn; import java.io.IOException; import java.util.Timer; pu

  • Java多线程定时器Timer原理及实现

    前言 定时/计划功能在Java应用的各个领域都使用得非常多,比方说Web层面,可能一个项目要定时采集话单.定时更新某些缓存.定时清理一批不活跃用户等等.定时计划任务功能在Java中主要使用的就是Timer对象,它在内部使用多线程方式进行处理,所以它和多线程技术关联还是相当大的.那和ThreadLocal一样,还是先讲原理再讲使用,Timer的实现原理不难,就简单扫一下就好了. Timer的schedule(TimeTask task, Date time)的使用 该方法的作用是在执行的日期执行一

  • Java定时器Timer使用方法详解

    一.概念 定时计划任务功能在Java中主要使用的就是Timer对象,它在内部使用多线程的方式进行处理,所以它和多线程技术还是有非常大的关联的.在JDK中Timer类主要负责计划任务的功能,也就是在指定的时间开始执行某一个任务,但封装任务的类却是TimerTask类. 通过继承 TimerTask 类 并实现 run() 方法来自定义要执行的任务: public class Mytask extends TimerTask { @Override public void run() { DateF

  • java定时器timer的使用方法代码示例

    1.首先肯定是容器一启动就要启动定时器,所以我们可以选择把定时器写在一个监听器里,容器一启动所以监听器也就跟着启动,然后定时器就可以工作了. 第一步,把自己写的监听器加到web.xml中: 第二步,写一个监听器,实现ServletContextListener接口: 第三步,写一个定时器,继承TimerTask,在复写的run()方法里写具体的业务逻辑. 第四步,在自己的监听器里复写的 public void contextInitialized(ServletContextEvent arg0

  • Java线程Timer定时器用法详细总结

    定时/计划功能主要使用的就是Timer对象,它在内部还是使用多线程的方式进行处理,所以它和线程技术还是有非常大的关联. Timer类主要作用就是设置计划任务,但封装任务的类却是TimerTask类.TimerTask类是一个抽象类. 执行任务的时间晚于当前时间-----在未来执行的效果 import java.util.Date; import java.util.TimerTask; public class MyTask extends TimerTask{ @Override public

  • Java中的定时器Timer详解

    目录 总结 简单来说,定时器就相当于一个"闹钟",给定时器设定一个任务,约定这个任务在xxx时间之后执行~ Timer类提供了一个核心接口,schedule(安排) 指定一个任务交给定时器,在一定时间之后再去执行这个任务~ 如何实现定时器的效果~ Timer中要包含一个Task类,每个Task就表示一个具体的任务实例,Task里面包含一个时间戳(啥时候执行这个任务),还包含一个Runnable实例(用来表示任务具体是啥). Timer里面通过一个带优先级的阻塞队列,来组织如干个task

  • Java中自动生成构造方法详解

    Java中自动生成构造方法详解 每个类在没有声明构造方法的前提下,会自动生成一个不带参数的构造方法,如果类一但声明有构造方法,就不会产生了.证明如下: 例1: class person { person(){System.out.println("父类-person");} person(int z){} } class student extends person { // student(int x ,int y){super(8);} } class Rt { public st

  • 基于Java中的StringTokenizer类详解(推荐)

    StringTokenizer是字符串分隔解析类型,属于:Java.util包. 1.StringTokenizer的构造函数 StringTokenizer(String str):构造一个用来解析str的StringTokenizer对象.java默认的分隔符是"空格"."制表符('\t')"."换行符('\n')"."回车符('\r')". StringTokenizer(String str,String delim)

  • JAVA中string数据类型转换详解

    在JAVA中string是final类,提供字符串不可以修改,string类型在项目中经常使用,下面给大家介绍比较常用的string数据类型转换: String数据类型转换成long.int.double.float.boolean.char等七种数据类型 复制代码 代码如下: * 数据类型转换 * @author Administrator * */ public class 数据类型转换 { public static void main(String[] args) { String c=

  • java 中 阻塞队列BlockingQueue详解及实例

    java 中 阻塞队列BlockingQueue详解及实例 BlockingQueue很好的解决了多线程中数据的传输,首先BlockingQueue是一个接口,它大致有四个实现类,这是一个很特殊的队列,如果BlockQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态,直到BlockingQueue进了东西才会被唤醒.同样,如果BlockingQueue是满的,任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有空间才会被唤醒继续操作.

  • Java中BigDecimal的基本运算(详解)

    BigDecimal一共有4个够造方法,让来看看其中比较常用的两种用法: 第一种:BigDecimal(double val) Translates a double into a BigDecimal. 第二种:BigDecimal(String val) Translates the String repre sentation of a BigDecimal into a BigDecimal. 使用BigDecimal要用String来够造,要做一个加法运算,需要先将两个浮点数转为Str

  • java 中 ChannelHandler的用法详解

    java 中 ChannelHandler的用法详解 前言: ChannelHandler处理一个I/O event或者拦截一个I/O操作,在它的ChannelPipeline中将其递交给相邻的下一个handler. 通过继承ChannelHandlerAdapter来代替 因为这个接口有许多的方法需要实现,你或许希望通过继承ChannelHandlerAdapter来代替. context对象 一个ChannelHandler和一个ChannelHandlerContext对象一起被提供.一个

  • Java中的反射机制详解

    Java中的反射机制详解 反射,当时经常听他们说,自己也看过一些资料,也可能在设计模式中使用过,但是感觉对它没有一个较深入的了解,这次重新学习了一下,感觉还行吧! 一,先看一下反射的概念: 主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义. 反射是Java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接.但是反射使用不当会成本很高! 看概念很晕的,继续往下

  • java 中匿名内部类的实例详解

    java 中匿名内部类的实例详解 原来的面貌: class TT extends Test{ void show() { System.out.println(s+"~~~哈哈"); System.out.println("超级女声"); } TT tt=new TT(); tt.show(); 只是说我们这里采用的是匿名的形式来处理. 重写了Test的show()方法,在重写好了以后,又调用了重写后的show()方法 实现代码: package cn.com; c

  • Java中isAssignableFrom的用法详解

    class1.isAssignableFrom(class2) 判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口.如果是则返回 true:否则返回 false.如果该 Class 表示一个基本类型,且指定的 Class 参数正是该 Class 对象,则该方法返回 true:否则返回 false. 1. class2是不是class1的子类或者子接口 2. Object是所有类的父类 一个例子搞定: package com.auuz

随机推荐