JAVA多线程代码案例----实现定时器

JAVA中的定时器

在java标准库中,也有定时器的实现。

Timer,Timer的schedule有两个参数,第一个是要执行的代码,第二个是等待时间。

public class Thread24 {
    public static void main(String[]args){
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {//时间到后,要执行的代码。
                System.out.println("Test");
            }
        },3000);//等待时间
    }
}

程序会在3秒后打印Test,一般代码是不会主动结束的,需要用cancle方法主动结束。

实现一个Timer

Timer里面的内容:

1)需要有一个线程,负责记录时间,等任务到达合适时间,这个线程就负责执行。

2)需要有一个队列/数组,用来保存schedule进来的任务。

但是如果队列很长,遍历的过程开销就会很大。

所以可以使用优先级数列,优先级数列中只需要关注队首元素是否到时间,如果队首没到时间,后续元素也一定没到时间,就不必遍历队列了。

就可以使用标准库提供的PriorityQueue(线程不安全,但可以通过手动加锁来控制),还有PriorityBlockingQueue(线程安全,但是不好控制),所以使用第一个。

时间戳:以1970年1月1日为基准,计算当前时刻和基准时刻的,秒数/毫秒数/微秒数.......之差。

我们引入时间戳来确定什么时间执行任务。

而且在多线程情况下,我们还要通过加锁来保证线程安全,因为设计到队列的取元素和删除元素。

package Thread;

import java.util.Objects;
import java.util.PriorityQueue;
class MyTimerTask implements Comparable<MyTimerTask>{
    private long time;//任务执行时间
    private Runnable runnable;

    public MyTimerTask(long delay, Runnable runnable) {
        this.runnable = runnable;
        this.time = System.currentTimeMillis()+delay;//由于delay是相对时间。
    }
    public void run(){
        runnable.run();
    }
    //任务执行的代码

    @Override
    public int compareTo(MyTimerTask o) {
        return (int)(this.time-o.time);
    }
    public long getTime(){
        return time;
    }
}
class MyTimer{
    private Thread t = null;//扫描线程
    private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();//任务队列
    Object lock = new Object();
  public void schedule(Runnable runnable,long delay){
     synchronized (lock){
      MyTimerTask task = new MyTimerTask(delay,runnable);
      queue.offer(task);
      //添加任务后,就可以唤醒扫描线程的wait了
      lock.notify();
    }
  }
  //构造方法,创建扫描线程,让扫描线程来完成判定和执行
    public MyTimer(){
      t = new Thread(()->{
          //扫描线程需要反复扫描队列首元素,判断首元素时间是否到了
          //没到,啥也不干
          //到了,执行任务并把这个任务删除
          while(true) {
              try {
                  synchronized (lock) {
                      while(queue.isEmpty()) {
                          lock.wait();//如果队列为空,继续获取元素是无意义的,所以要加上wait。
                      }
                      MyTimerTask task = queue.peek();
                      long curTime = System.currentTimeMillis();
                      if (curTime >= task.getTime()) {
                          //时间到了,可以执行了
                          queue.poll();
                          task.run();
                      } else {
                         lock.wait(task.getTime()-curTime); //时间没到,暂时不执行
                      }
                  }
              }catch (InterruptedException e){
                  e.printStackTrace();
              }
          }
      });
      t.start();
    }
}
public class Thead25 {
    public static void main(String[]args){
        MyTimer timer = new MyTimer();
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello 3000");
            }
        },3000);
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello 2000");
            }
        },2000);
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello 1000");
            }
        },1000);
        System.out.println("hello main");
    }
}

有一说一,这个代码确实麻烦,而且涉及到wait和notify,导致代码并不是按照我们认知中的顺序执行,需要我们有多线程思想,对于我这个初学者确实麻烦...........

代码的运行结果应该是这样的:

hello main
hello 1000
hello 2000
hello 3000

而且由于线程是在MyTimer里创建的,所以它并不会主动终止。

代码是按照任务的等待时间大小来执行的,不是按照任务创建的先后执行的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值