详解 定时器 的基本实现

shadowLogo

在本人之前的博文中所讲解过定时器的问题
有很多同学可能会很好奇它是如何实现的
那么,在本篇博文中,本人将简单来实现下定时器:

基本思路:

首先呢,我们来思考这样的问题 :

我们该如何实现代码段延迟执行呢?

对于 右转哥的铁粉 以及 JavaSE功底比较棒的同学来说,
这个问题很简单 —— 线程等待


那么,根据以上思想
本人来给出一个通过线程等待的方式实现的延迟执行定时器

实现代码:

package edu.youzg.timer_impl.core;

public abstract class DidaDida implements Runnable{
    public static final long DEAFLT_DELAY = 1000;	// 默认延迟时间
    private long delay;	// 延迟时间
    private volatile boolean goon;	// 用于 开启/关闭线程
    private Object lock;	// 避免线程安全问题 的锁

    public DidaDida() {
        this(DEAFLT_DELAY);
    }

    public DidaDida(long delay) {
        this.lock = new Object();
        this.delay = delay;
    }

    public void setDelay(long delay) {
        this.delay = delay;
    }

    public abstract void doing();

    public void start() {
        if (this.goon == true) {
            return;
        }

        this.goon = true;
        new Thread(this).start();
    }

    public void  stop() {
        if (this.goon==false) {
            return;
        }
        this.goon = false;
    }

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

    private class InnerWorker implements Runnable{
        public InnerWorker() {
            new Thread(this).start();
        }

        @Override
        public void run() {
            doing();
        }
    }
}

优化思路:

优化方案:

在上面的代码中,主要应用了Object的对象充当
但是,在本人《详解 锁》一文中,讲到了:
JDK1.5之后,Java提供了一个专门针对同步互斥接口 —— Lock接口 和它的实现类们

在此处,我们只需要运用到:

lock.lock();
需要同步的代码块;
lock.unlock();

来实现同步


运用

condition.await(long time,TimeUnit unit)

代替

object.wait(long timeout)

扩展:

原生方法 封装优化方法
Object类 中的
wait()方法
Condition类 中的
await()方法
Object类 中的
wait(long timeout)方法
Condition类 中的
await(long time,TimeUnit unit)方法
Object类 中的
notify()方法
Condition类 中的
singal()方法
Object类 中的
notifyAll()方法
Condition类 中的
singalAll()方法
Thread类 中的
静态方法
sleep()
(格式:Thread.sleep(休眠时长大小))
TimeUnit类 中的
静态方法
sleep()
(格式:TimeUnit.单位枚举.sleep(休眠时长大小))

那么,本人就运用Lock接口api来优化下上文中的代码:

优化版本:

package edu.youzg.timer_impl.core;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author: Youzg
 * @CreateTime: 2020-07-17 09:23
 * @Description: 带你深究Java的本质!
 */
public abstract class Didadida implements Runnable {
    private static final long DEFAULT_DELAY_TIME = 1000;    // 默认延迟时间

    private volatile boolean goon;  // 用于 开启/关闭线程
    private long delay; // 延迟时间
    private Lock lock;  // 安全“锁”对象
    private Condition condition;    // 用于控制 当前线程 的 状态

    public Didadida() {
        this(DEFAULT_DELAY_TIME);
    }

    public Didadida(long delay) {
        this.delay = delay;
        this.lock = new ReentrantLock();
        this.condition = lock.newCondition();
    }

    public void setDelay(long delay) {
        this.delay = delay;
    }

    public void start() {
        if (this.goon) {
            return;
        }
        this.goon = true;
        new Thread(this).start();
    }

    public void stop() {
        if (!this.goon) {
            return;
        }
        this.goon = false;
    }

    protected abstract void doTask();

    @Override
    public void run() {
        while (this.goon) {
            lock.lock();
            try {
                // await() 方法释放对象锁
                // 参1 ———— 大小
                // 参2 ———— 单位(此处设置的单位是 毫秒)
                this.condition.await(this.delay, TimeUnit.MILLISECONDS);    // 1. 被唤醒 2. 超过设定的时间 3. 阻塞状态
                new InnerWorker();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    private class InnerWorker implements Runnable {
        public InnerWorker() {
            new Thread(this).start();
        }

        @Override
        public void run() {
            doTask();
        }
    }

}

使用展示:

那么,现在本人来给出一个使用该定时器来完成指定指令的类:
(本人要求每隔0.5秒输出一下这条语句,并且总共运行2s)

package edu.youzg.timer_impl.test;

public class DemoDidadida {
    private Didadida dida;
    private static int times = 0;

    public DemoDidadida() {
        dida = new Didadida(2000) {
            @Override
            public void doTask() {
                int time = ++times;
                System.out.println("第" + time + "次开始:" + System.currentTimeMillis());
            }
        };
    }

    public void runup() {
        dida.start();
        try {
            Thread.sleep(10000); //这里我们设置总共运行10s
        } catch (InterruptedException e) {
        }
        dida.stop();
    }

}

现在,本人来给出一个测试类:

package edu.youzg.timer_impl.test;

public class Test {
    
    public static void main(String[] args){
        new DemoDidaDida().runup();
    }
    
}

那么,现在本人来展示下运行结果
定时器 展示
可以看到,结果的确按照我们的要求 —— 每隔2s(2000ms)输出一次

但是又存在 些微误差,这是因为我们开启线程 等语句也是会耗时
但是,大体上来看,这些误差还是能够被忽略

以上就是 定时器基本实现步骤了!
那么,在本人之后的博文中,也会对这里的工具代码有所应用,
希望同学们能够领悟到本篇博文的思想


最后,想要了解JDK版本源码实现的同学,可以观看如下博文:

JDK源码剖析:

请观看博文 —— 《【源码剖析】定时器 详解》

posted @ 2020-06-13 19:00  在下右转,有何贵干  阅读(101)  评论(0编辑  收藏  举报