多线程案例——定时器

目录

一、定时器

1. 标准库中的定时器

2. 自己实现一个定时器


 

一、定时器

   定时器类似一个闹钟,经过指定的时间后执行要求的代码。

1. 标准库中的定时器

  • Timer 类 
  • Timer 类 的 schedule 方法 

   通过 schedule 方法,传入要执行的任务(TimerTask类,专门用来描述任务) 以及 要执行的时间后,就实现了一个定时器。 

   具体使用如下: 

public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        },3000);

        System.out.println("man");
    }

2. 自己实现一个定时器

   大致思路:

  • 1. 描述一个任务: Runnable + time
  • 2. 使用优先级队列来组织若干个任务:PriorityBlockingQueue
  • 3. 实现 schedule 方法来将任务放入优先级队列中
  • 4. 创建一个线程,用这个扫描线程不断获取队列的队首元素,并且判断时间是否到达
  • 5. 注意:① 让 MyTimerTask 支持比较;② 解决线程的忙等问题 

   更加详细的解释:

  • 1. 描述一个任务的类,要求存放要执行的具体任务(Runnable类)以及 多长时间后要执行(after):MyTimerTask。
  • 2. 管理任务的数据结构,因为是定时器,所以要求要能获取到最短时间的任务 —— 优先级队列 (PriorityQueue) —— 因为涉及到阻塞 —— PriorityBlockingQueue。
  • 3. 定时器的主要方法:schedule —— 将任务加入定时器。
  • 4. 中定时器需要不断地获取当前时间,并且判断是否到了第一个要执行任务的时间 —— 在定时器中需要有一个额外的线程进行不断的循环判断。

   大体框架写好后,还要注意:

  • 1. 优先级队列中存放的是我们新建的任务类的对象,而在优先级队列中,需要比较每个任务的执行时间来进行排序 —— 在任务类中继承 Comparable 接口,实现 compareTo 方法
  • 2. 在定时器中的线程中,不断高速循环判断队列中的第一个元素,当当前时间里目标时间很大时,在这段空闲时间中,这样不断访问判断会占用大量的 CPU 资源,属于忙等。因此,我们需要指定等待时间 —— wait(等待时间),先判断第一个队列中的任务,然后进行设置等待时间,接下来线程就进入阻塞状态,等时间到了才会继续进行访问
  • 3. 在线程等待的过程中,可能会有其他任务加入到队列中,但如果新加入的任务的时间比队列中第一个任务的时间还要早时,我们就要先执行这个新加入的任务了。因此,考虑到这种情况,我们每加入一个元素,就要将线程唤醒一次,让线程继续判断这个新加入的任务 —— notify(),当线程没有在阻塞中也无所谓

   具体代码:

class MyTimerTask implements Comparable<MyTimerTask> {
    private Runnable runnable = null;
    private long time = 0;

    public MyTimerTask(Runnable runnable, int after) {
        this.runnable = runnable;
        this.time = after + System.currentTimeMillis();
    }

    public long getTime() {
        return time;
    }

    public void run() {
        runnable.run();
    }

    public int compareTo(MyTimerTask o) {
        //要让时间大的在后,时间小的在前(升序)
        return (int) (this.time - o.time);
    }
}

//定时器
class MyTimer {
    private Object locker = new Object();
    private PriorityBlockingQueue<MyTimerTask> queue = new PriorityBlockingQueue<>();

    public void schedule(Runnable runnable, int after) {
        MyTimerTask task = new MyTimerTask(runnable, after);
        queue.put(task);
        synchronized (locker) {
            locker.notify();
        }
    }

    //构造函数中,创建一个线程,不断循环任务队列,看时间是否到了
    public MyTimer() {
        Thread thread = new Thread(() -> {
            while(true) {
                try {
                    MyTimerTask task2 = queue.take();
                    long curtime = System.currentTimeMillis();
                    if (curtime < task2.getTime()) {
                        queue.put(task2);
                        synchronized (locker) {
                            locker.wait(task2.getTime() - curtime);
                        }
                    } else {
                        //时间到了,执行 run
                        task2.run();
                    }

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });
        thread.start();
    }

}

public class Test {
    //测试
    public static void main(String[] args) {
        MyTimer timer = new MyTimer();
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        }, 3000);

        //用main 线程进行对比
        System.out.println("main");
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值