定时器介绍

发布于:2025-02-23 ⋅ 阅读:(107) ⋅ 点赞:(0)

 定时器:

定时器就相当于闹钟,时间一到就执行相关的任务。

1.标准库中提供了一个Timer类,Timer类的核心方法是为schedule。

2.在schedule方法中有两个参数,第一个参数指定即将要执行的任务代码,第二个参数指定多长时间之后执行。

正常描述任务用Runnable,这里用TimerTask包装了一下

 核心还是重写run方法。

import java.util.Timer;
import java.util.TimerTask;
public class Test{
    public static void main(String[] args) {
        Timer timer=new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("3s后执行任务");
            }
        },3000);
    }
}

定时器的实现:

1.首先创建一个类MyTimerTask,用来表示任务。

2.定时器中,使用阻塞队列将多个任务管理起来(确保时间最早的要最先执行)优先级队列是优选的

3.定时器中的schedule方法,目的把任务添加到队列中。

4.在定时器中额外创建一个线程,负责执行队列中的任务。(和线程池不同,线程池只要阻塞队列不为空就读取任务并继续执行,此处还要检查任务的执行时间是否到了,到了时间才执行

在将任务添加到队列中时,必须要明确比较规则(让时间最早的任务在队首,而时间最晚的任务在队尾)

注意:使用该方法,要实现Comparable接口。这个方法是交给阻塞队列去调用的。(只需写好规则即可)

import java.util.PriorityQueue;

class MyTimerTask implements Comparable<MyTimerTask>{
    private Runnable runnable;//需要执行的任务
    private long time;//执行任务的时间
    public MyTimerTask(Runnable runnable, long time){
        this.runnable=runnable;
        this.time=time;
    }

    @Override
    public int compareTo(MyTimerTask o) {//这是提供给阻塞队列的方法,用来排列执行任务的先后顺序
        return (int) (this.time-o.time);
    }

    public long getTime() {
        return time;
    }
    public void run(){
        runnable.run();
    }
}
class MyTimer{
    private PriorityQueue<MyTimerTask> queue=new PriorityQueue<>();
    public void schedule(Runnable task,int time){
        synchronized (this){
            MyTimerTask tk=new MyTimerTask(task,System.currentTimeMillis()+time);
            queue.offer(tk);
            this.notify();
        }
        }
        public MyTimer(){//构造方法本身也可以加synchronized,但是需要保护的是线程里面的方法
        Thread t=new Thread(()->{
            while(true){
                synchronized (this){
                    while(queue.isEmpty()){//如果阻塞队列为空,就一直等待
                        try {
                            this.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    MyTimerTask task=queue.peek();
                    if(task.getTime()>System.currentTimeMillis()){//执行任务的时间和系统时间比较
                        try {
                            this.wait(task.getTime()-System.currentTimeMillis());
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else{//说明到了执行任务的时间
                        task.run();
                        queue.poll();
                    }
                }
            }
        });
        t.start();
        }
    }

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

为什么在schedule中要加synchronized:因为在schedule方法是一个线程,但是定时器内部构造方法还有一个线程,当两个线程同时操作队列时,就要涉及到线程安全问题。

带有定时器的线程池:

要创建一个带有定时器的线程池,可以使用Java中ScheduledThreadPoolExecutor的类。下面是一个示例代码:

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Test2 {
    public static void main(String[] args) {
        ScheduledExecutorService stpe= Executors.newScheduledThreadPool(3);
        stpe.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("我第一个执行 1000");
            }
        },1, TimeUnit.SECONDS);
        stpe.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("我第二个执行 2000");
            }
        },2, TimeUnit.SECONDS);
        stpe.shutdown();//关闭线程池
    }
}

在上面的代码中,我们使用Executors.newScheduledThreadPool方法创建了一个带有定时器的线程池,指定了最大线程数为3。然后,我们使用executor.schedule方法提交了两个任务,并指定了延迟执行的时间和时间单位。这两个任务将在指定的时间后被执行。

最后,我们调用executor.shutdown方法关闭线程池。


网站公告

今日签到

点亮在社区的每一天
去签到