(多线程)定时器

目录

🍀标准库提供的方法

🍂代码

🍂执行结果

🍀自己实现一个定时器

🍂设计思路

🍂代码

🍋Timertask

🍋Timer

🍂思考

🍂运行结果


标准库提供的方法

       定时器,顾名思义,我们可以设置一个任务,要求在多长时间时候执行。可以布置好几个任务,哪个任务的时间到了执行哪一个

我们可以使用标准库中的Timer类来实现定时器方法。

代码

public class TestTimer {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("这是第一个任务");
            }
        },1000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("这是第二个任务");
            }
        },2000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("这是第三个任务");
            }
        },3000);
    }
}

这里面 TimerTask实际上也就是一个Runnable,会重写其中的run方法

执行结果

可以看到当三个任务执行结束之后程序并没有退出,而是阻塞等待

自己实现一个定时器

设计思路

       根据标准库中的代码,我们自己实现定时器时也可以使用这种思路,首先定时器既然要达到定时的效果,就要判断添加到任务执行的时间顺序,所以我们先确定要使用一个带有优先级并且有阻塞功能的队列来存放每个任务,所以我们就可以直接使用PriorityBlockingQueue。其次我们设计的Timer类中也可以使用schedule方法来添加任务,在构造方法里创建线程进行任务的具体执行。

       参考标准库方法,我们可将具体任务抽象出来一个类(Timertask)。那么类的属性就有执行的时间time和Runnable等属性。 这里因为我们需要将添加进来的任务进行一个比较排序,所以time需要使用绝对的时间戳加上任务设计规定的时间

      我们的任务存在PriorityBlockingQueue中,也就是存的每个元素是一个可比较的Timertask,所以Timertask需要实现Comparable接口重写compareTo方法

      其他实现的具体细节我们边写代码边分析。

代码

Timertask

class Timertask implements Comparable<Timertask>{
    //时间使用绝对的时间戳
  public long time;
  public Runnable runnable;

  public Timertask(Runnable runnable,long time){
      this.runnable = runnable;
      //绝对时间戳加上相对时间time得到的时间就是当前任务要执行的实际时间
      this.time = System.currentTimeMillis()+time;
  }

    @Override
    public int compareTo(Timertask o) {
        return (int) (this.time-o.time);
    }
}

这个类来表示一个具体要进行的任务。

Timer

public class MyTimer {
    //带有优先级的阻塞队列
    PriorityBlockingQueue<Timertask> blockingQueue = new PriorityBlockingQueue<>();
    //创建锁对象
    Object clock = new Object();
    public void schedule(Runnable runnable,long time){
        //这里面的time参数填的是形如1000,2000的相对时间,表示当前时间的基础上过多久在执行任务
        //构造一个任务,并添加进队列
        Timertask timertask = new Timertask(runnable,time);
        blockingQueue.put(timertask);

        synchronized (clock){
            clock.notify();
        }
    }

    //创建线程去执行任务
    public MyTimer(){
        Thread t = new Thread(()->{
           while (true){
               try {
                   synchronized (clock) {

                       Timertask ts = blockingQueue.take();
                       //当前的时间
                       long curtime = System.currentTimeMillis();
                       if (curtime >= ts.time) {
                           //到了执行任务的时间
                           ts.runnable.run();
                       } else {
                           //时间还没到, 将取出的任务又重新放回队列中
                           blockingQueue.put(ts);
                           //为了避免忙等
                           clock.wait(ts.time - curtime);

                       }
                   }
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        });
        //别忘了启动线程
        t.start();
    }

代码的整体思路与我们上面的思路差不多,也有几处细节问题需要思考一些

思考

这两边的代码只是加锁的地方不同,思考这两种会有什么区别

       左边的加锁是正确的,而右边的加锁则会造成两个问题:1.notify会造成空打一炮的问题,也就是还没有wait阻塞等待,就先notify解等待了。2.会错过一些任务没有执行到。

 造成这一问题的罪魁祸首还是因为线程的随机调度和无序抢占式执行

运行结果

class Main{
    public static void main(String[] args) {
        MyTimer myTimer = new MyTimer();
        myTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello 3");
            }
        },3000);
        myTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello 2");
            }
        },2000);
        myTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello 1");
            }
        },1000);
        System.out.println("hello 0");
    }
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,可以使用多线程定时器来实现定时任务。你可以通过创建一个继承自Thread类或实现Runnable接口的类来定义一个线程,并在run方法中编写线程的执行逻辑。然后,使用定时器类Timer或ScheduledThreadPoolExecutor来调度线程的执行。 下面是一个使用定时器类Timer实现定时任务的示例代码: ```java import java.util.Timer; import java.util.TimerTask; public class MyTask extends TimerTask { public void run() { // 在这里写定时任务的逻辑 System.out.println("定时任务执行了"); } public static void main(String[] args) { Timer timer = new Timer(); MyTask task = new MyTask(); // 延迟1秒后开始执行任务,每隔2秒执行一次 timer.schedule(task, 1000, 2000); } } ``` 在上面的示例中,首先定义了一个继承自TimerTask的类MyTask,重写了run方法,在run方法中编写了定时任务的逻辑。然后,在main方法中创建了一个Timer对象和一个MyTask对象,使用schedule方法指定了任务的延迟时间和执行间隔。 当程序运行时,定时任务会在延迟1秒后开始执行,并且每隔2秒执行一次。你可以根据实际需求修改延迟时间和执行间隔。 除了使用Timer类,你还可以使用ScheduledThreadPoolExecutor类来实现定时任务。这个类提供了更灵活的定时任务调度方式,可以满足更多复杂的需求。你可以根据自己的需求选择适合的方式来实现Java多线程定时器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值