滴答滴答(TickTick)定时器工具

此博客属于原创,若哪里有错,欢迎指出!!

在编程中,关于定时这个概念,所作用的方面是十分的宽广的。对于需要在每隔一定的时间,就需要处理一些事情的场景也是随处可见的,无论是生活中,还是编程中。为了面对这样生活中,编程中的需要,关于定时器的工具的编写,也就显得有必要了起来。
于是我们今天来用java来实现这个工具的编写。

在编写代码之前,我们先讨论一下关于这个我们要编写的定时器工具需要完成的事情。
我们所编写的定时器的作用是:每隔一段时间,就执行一些操作。毫无疑问,首先,关于这个间隔的时间是我们的用户,通过函数的参数输入进去的,间隔的时间应该是由用户根据自己的需要自己规定的。其次就是关于规定的间隔时间到了,应该要做的事情应该也是通过用户自己根据自己具体的需要,来编写的。通过讨论,我们的目的已经十分清楚了,总结一下就是:

目的:我们要编写一个由用户传入间隔的时间(以毫秒为单位)作为参数,然后我们的定时器就会每隔这个时间,就会执行由用户所规定的事情。

目的已经明确决定的现在,我们要做的事情就是来仔细的考虑这个工具的具体实现的过程。
首先,毫无疑问的我们的定时器的线程应该不停地跑,应该独立于主线程的。所以定时器这个类是需要实现线程的创建的。

其次,关于如何做到经过规定的时间,也就是定时,我们可以采用Object类的wait(long timeOut)这个方法。这个方法的具体内容是:哪一个线程调用了一个Object类的这个方法,就会使这个线程阻塞在这个Object上,形成阻塞队列,进入阻塞状态。经过timeOut时间后,被计算机操作系统所唤醒,线程从阻塞态变为就绪态,此时wait(long timeOut)方法执行完毕,然后该进程与其他的众多线程在一起,等待进程调度来调用此进程,使此进程中的代码再次被执行。关于wait方法的使用,必须将wait方法放在锁中,才能调用wait方法。

也就是说可以通过java提供给我们的方法wait(long timeOut)方法来实现我们的时间的间隔(事实上这样的时间的间隔并不是完美的精确,因为wait方法是等待了用户所规定的的相应的时间,但是wait方法执行完毕后,此进程被cpu再次的调用才能执行之后的方法。因此这里存在一个等待系统调用的时间,而这个时间的长短只取决于目前所运行的进程总数与几核cpu 的问题,这个时间可以通过升级硬件,关闭其他程序等方法来减少,但是无法彻底的根除掉这个时间,因此这个时间的会导致实际间隔时间比我们所输入的时间要稍微长一点点。)

当然这个定时器需要每隔时间都要做一些事情,因此我们应该将这个wait方法的执行放入一个循环之中,关于循环的控制,也就是什么时候停止,我们应该给用户决定的机会。

具体的代码如下:
滴答滴答(TickTick)定时器工具
然后,我们要处理关于执行由用户所规定的方法,非常容易想到,这里可以使用抽象类abstract来处理这个问题,将具体要做的事情抛给用户让用户来执行。

规定一个抽象方法,在我们的程序中来调用这个方法,但是这个方法要求用户来实现。但是要注意的是:或许大部分人会将这个方法的执行放在wait方法的后面,第48行。如果这样处理的话,用户所规定的的方法的执行需要花费时间,因此就导致实际的间隔时间可能与我们所规定的时间要长。

要调用用户所要执行的方法,但又不会大幅度的影响我们的定时器本身的定时工作,因此我们选择再创建一个线程,让这个线程来实现用户所需要的方法。这里我们可以通过内部类来创建另一个线程,通过这个线程的run方法执行方法。这个线程与定时器的线程存在一个关系:定时器线程完成了一次定时,另执行用户方法的线程就应该开始执行。所以执行方法的线程需要先将自己阻塞,再通过定时器线程的定时结束后,唤醒执行方法的线程,开始执行相应的方法,方法执行结束后,就应该再次阻塞,等待定时器线程的再次唤醒。通过思考后,这个执行方法的线程的阻塞自己与执行方法的步骤,也是循环持续执行的,结束的条件当然是:定时器线程结束时。定时器线程结束后,这个执行方法的线程也不需要继续跑了。

已经分析完毕后,我们可以开始编写这个内部类了:
滴答滴答(TickTick)定时器工具
第70行的这个doSomething()方法就是抽象方法需要用户自己完成的方法。还有关于这个doSomething()的这个方法。

做到这里,可以说我们的定时器的关键的步骤就都结束了,还有最后一点,关于两个线程的循环结束的这个条件的boolean变量goon的定义是需要加上volatile这个关键字的修饰,是为了防止jvm的寄存器优化的问题。

完整的代码如下:

public abstract class TickTick implements Runnable{
   	private Object lock;
    private volatile boolean goon;
    private long timeOut;
    private static long DEFAULT_TIMINGOUT = 1000;

   public TickTick() {
        lock = new Object();
        timeOut = DEFAULT_TIMINGOUT;
        goon = false;
    }

   public TickTick setTiming(long timeOut) {
        this.timeOut = timeOut;

   return this;
    }

   public void startup() {
        System.out.println("goon:" + goon);
        if (goon) {
            System.out.println("TickTick,定时器已经启动!!");
            return;
        }
        goon = true;
        new TickTickWorker();
        new Thread(this, "TickTick").start();
    }

   public void stop() {
        if (!goon) {
            System.out.println("TickTick,定时器已经关闭!!");
        }
        goon = false;
    }

   public abstract void doSomething();

   @Override
    public void run() {
        while (goon) {
            synchronized (lock) {
                try {
                    lock.wait(timeOut);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

   class TickTickWorker implements Runnable {
        public TickTickWorker() {
            new Thread(this, "TickTickWorker").start();
        }

   @Override
      public void run() {
            while (goon) {
                synchronized (lock) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                doSomething();
            }
        }
    }
}

此博客为本人第一次写的博客,若哪里有错,欢迎指出!!

相关文章: