Multi-threading case-timer (with complete code)

What is a timer

The timer is an important component in software development. It is similar to an "alarm clock".After a set time is reached, a specified code is executed. .

Timer is a very commonly used component in actual development.

For example, in network communication, if the other party does not return data within 500ms, it will disconnect and try to reconnect.

For example, in a Map, we hope that a certain key in it will expire (automatically deleted) after 3 seconds.

Scenarios like this require the use of timers.

Timers in the standard library

The standard library provides a Timer class. The core method of the Timer class is schedule.

schedule contains two parameters. The first parameter specifies the task code to be executed, and the second parameter specifies how long it will take to execute (in milliseconds)

Here is the code example:

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

        System.out.println("hello main");
    }
}

Running the code, we can observe that "hello main" is printed directly at first, and then "hello timer" is executed three seconds later. Why does this happen? Apparently each Timer has a built-in thread.

 Note: Since Timer has a built-in thread (foreground thread), it will prevent the process from ending. This is because the timer does not know whether the code will add new tasks, so it is in a state of readiness.

Therefore, the cancel() method needs to be used here to actively end it, otherwise the timer does not know where else tasks will be added.

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

        System.out.println("hello main");
        Thread.sleep(4000);
        timer.cancel();
    }
}

At this time, you can observe that the process ended successfully.

 

Implement a timer

Timer composition

A queue with priority (Do not use PriorityBlockingQueue, it is easy to deadlock!), but use PriorityQueue

 Implementation principle:

1. Each element in the queue is a Task object.

2.Task has a time attribute, and the first element of the team is the task that is about to be executed.

3. At the same time, there is a worker thread that has been scanning the first element of the team to see whether the first element of the team needs to be executed (the execution time is small first, and then the execution time is long).

1. The core interface provided by the Timer class is schedule, which is used to register a task and specify how long it will take to execute the task.

public class MyTimer {
    public void schedule(Runnable runnable, long time) {
        //TODO
    }
}

2. The Task class is used to describe a task (as an internal class of Timer). It contains a Runnable object and a time (millisecond timestamp)

This object needs to be placed in the priority queue, so it needs to implement the Comparable interface.

PriorityQueue, TreeMap, and TreeSet all require elements to be "comparable in size". Comparable and Comparator are required.

HashMap and HashSet require elements to be "comparatively equal" and "hashable". Therefore, they need to be implemented

equals,hashCode方法

class MyTimerTask implements Comparable<MyTimerTask> {
    private Runnable runnable;
    //为了方便后续判定,使用了绝对的时间戳
    private long time;
    
    public MyTimerTask(Runnable runnable, long delay) {
        this.runnable = runnable;
        //取当前时刻的时间戳 + delay,作为该任务实际执行的时间戳
        this.time = System.currentTimeMillis() + delay;
    }

    @Override
    public int compareTo(MyTimerTask o) {
        //这样的写法意味着每次去除的是时间最小的元素
        //到底是谁减谁,不要记,建议随便写个试一试
        return (int)(this.time - o.time);
    }
}

3. In the Timer instance, several Task objects are organized through PriorityQueue.

Insert Task objects into the queue one by one through schedule

class MyTimer {
    //核心结构
    private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
    //创建一个锁对象
    private Object locker = new Object();
    
    public void schedule(Runnable runnable, long time) {
        //根据参数,构造MyTimerTask,插入队列即可
        synchronized(locker) {
            MyTimerTask myTimerTask = new MyTimerTask(runnable, delay);
            queue.offer(myTimerTask);
            locker.notify();
        }
    }
}

4. There is a worker thread in the Timer class, which constantly scans the first element of the queue to see if it can perform this task.

The so-called execution means that the time has arrived. 

//在这里构建线程,负责执行具体的任务了
public MyTimer() {
    Thread t = new Thread(() -> {
        while(true) {
            try {
                synchronized(locker) {
                    //阻塞队列,只有阻塞的入队列和阻塞的出队列,没有阻塞查看队首元素
                    while(!queue.isEmpty()) {
                        locker.wait();
                    }
                    MyTimerTask myTimerTask = queue.peek();
                    long curTime = System.currentTimeMillis();
                    if(curTime >= myTimerTask.getTime()) {
                        //时间到了,可以执行任务了
                        queue.poll();
                        myTimerTask.run();
                    } else {
                        //时间还没到
                        locker.wait(myTimerTask.time - curTime);
                    }
                }
            } catch(InterruptException e) {
                e.printStackTrace();
            }
        }
    });
    t.start();
}

 The complete code and analysis are attached below:

import java.util.PriorityQueue;

//通过这个类,来描述一个任务,这一整个任务,是要放到优先级队列中的
class MyTimerTask implements Comparable<MyTimerTask>{
    //在什么时间来执行这个任务
    //此处的time是一个ms级别的时间戳
    private long time;
    //实际任务要执行的代码
    private Runnable runnable;
    //delay期望是一个相对时间
    public MyTimerTask(Runnable runnable, long delay) {
        this.runnable = runnable;
        //计算一下要执行任务的绝对时间.(使用绝对时间,方便判定任务是否到达时间的)
        this.time = System.currentTimeMillis() + delay;
    }

    //实际执行代码的方法(一般在主函数中重写)
    public void run() {
        runnable.run();
    }

    //用于获得任务执行时间
    public long getTime() {
        return this.time;
    }

    //构建一个比较器(因为在优先级队列中是按时间对任务进行比较)
    @Override
    public int compareTo(MyTimerTask o) {
        return (int)(this.time - o.time);
    }
}

public class MyTimer{
    //构建一个线程
    private Thread t = null;
    //创建存放任务的主体--优先级队列
    private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
    //创建一个锁对象
    private Object locker = new Object();

    //结束进程的方法
    public void cancel() {
        t.interrupt();
    }
    //构造方法:以用于创建线程
    public MyTimer() {
        t = new Thread(() -> {
            while(!Thread.interrupted()) {
                try {
                    synchronized (locker) {
                        while(queue.isEmpty()) {
                            //当队列为空时,这个线程就一直处于阻塞等待的状态
                            locker.wait();
                        }
                        //获得队列头部元素
                        MyTimerTask task = queue.peek();
                        //计算系统当前时间
                        long curTime = System.currentTimeMillis();
                        //判断是否应该执行
                        if(curTime >= task.getTime()) {
                            //执行任务
                            queue.poll();
                            task.run();
                        } else {
                            //等待到指定时间再执行任务
                            locker.wait(task.getTime() - curTime);
                        }
                    }
                } catch (InterruptedException e) {
                    break;
                }
            }
        });
        t.start();
    }
    //通过这个方法来执行实际的任务
    public void schedule(Runnable runnable, long delay) {
        synchronized (locker) {
            //先创建一个任务,后将任务放入队列
            MyTimerTask myTimerTask = new MyTimerTask(runnable, delay);
            queue.offer(myTimerTask);
            //唤醒因为队列中因为没有任务而阻塞等待的线程
            locker.notify();
        }
    }
}

 

Guess you like

Origin blog.csdn.net/asdssadddd/article/details/134963703