Java ancient timer learning--Timer class

Java introduced the Timer tool class in version 1.3. It is an ancient timer and is used with TimerTask and TaskQueue . There are many fatal problems for his implementation , so another timer ScheduledThreadPoolExecutor was introduced in the concurrent package in java5.

Let's talk about the defects and characteristics of Timer:

1. Timer is a single thread , and its bottom layer is to create a thread by itself to execute the tasks in its own task queue.

Let's take a look at the source code

    //调用构造方法创建Timer对象时,线程就被启用
    Timer timer = new Timer;

    //....Timer类源码如下:
    //---The Timer thread;
    private final TimerThread thread = new TimerThread(queue);

    //-----Timer的构造方法
    public Timer(String name, boolean isDaemon) {
        var threadReaper = new ThreadReaper(queue, thread);
        this.cleanup = CleanerFactory.cleaner().register(this, threadReaper);
        thread.setName(name);
        thread.setDaemon(isDaemon);
        //启用线程,而对于java启用线程的方式是通过调用start()方法调用Thread类的实例对象中的run()方法
        thread.start();
    }

When the thread is called, execute the run() method

//...Timer的内部类
class TimerThread extends Thread {

    boolean newTasksMayBeScheduled = true;

    //任务队列
    private TaskQueue queue;

    TimerThread(TaskQueue queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            //线程主方法
            mainLoop();
        } finally {
            // Someone killed this Thread, behave as if Timer cancelled
            synchronized(queue) {
                newTasksMayBeScheduled = false;
                queue.clear();  // Eliminate obsolete references
            }
        }
    }

Next, let's take a look at the mainLoop( ) method under the run() method

    /**
     * The main timer loop.  (See class comment.)
     * 主计时器循环
     */
    private void mainLoop() {
        while (true) {
            try {
                TimerTask task; //任务队列中的任务
                boolean taskFired; //任务是否被触发
                synchronized(queue) {
                    
                    // 任务队列为空时会等待
                    //一旦newTasksMayBeScheduled标志为 true,并且我们的队列中没有更多任务,    
                    //我们就没有工作要做,因此我们优雅地终止。
                    while (queue.isEmpty() && newTasksMayBeScheduled)
                        queue.wait();

                    if (queue.isEmpty())
                        break; // Queue is empty and will forever remain; die

                    // Queue nonempty; look at first evt and do the right thing
                    long currentTime, executionTime;
                    task = queue.getMin(); //从任务队列中取出第一个任务
                    synchronized(task.lock) {
                        if (task.state == TimerTask.CANCELLED) { //判断任务状态
                            queue.removeMin(); //移走任务
                            continue;  // 继续循环查找任务
                        }

                        currentTime = System.currentTimeMillis();
                        executionTime = task.nextExecutionTime;
                        //判断任务是否过期
                        if (taskFired = (executionTime<=currentTime)) { 
                            if (task.period == 0) { // 是否会再次执行
                                queue.removeMin();
                                task.state = TimerTask.EXECUTED;
                            } else { //重复任务,重复安排
                                queue.rescheduleMin(
                                  task.period<0 ? currentTime   - task.period
                                                : executionTime + task.period);
                            }
                        }
                    }
                    if (!taskFired) //任务未触发,等待
                        queue.wait(executionTime - currentTime);
                }
                if (taskFired)  //任务被触发,运行
                    task.run();
            } catch(InterruptedException e) {
            }
        }
    }

In fact, this method is to continuously judge the status of the task in the task queue and whether it is triggered, and whether it needs to be executed repeatedly . If there is no task in the queue or the task in hand is not yet triggered , it will wait in the task queue , and then judge again, so as to execute the business of the timer.

And because it is an independent thread, and its own loop mechanism. You can make it wait for the task to come on the premise that the task does not make mistakes . But this also inevitably leads to the fact that if one of the tasks crashes, the entire timer will have to hang up. Therefore, the risk is very high, and it is not suitable for high concurrency scenarios.

2. While the timer loop is waiting for tasks, other threads can call its reference to add tasks to the queue. It is equivalent to a machine that is always working, we can throw things to him for processing, and when there is no task, as long as there is no error, the machine can also keep running and waiting. When a task is added to the queue, it will wake up the task queue, that is, wake up the conveyor belt that dropped the task to this machine, and this machine will pull the task and judge whether it is executed or not.

Here is the source code:

Because there are many ways to add tasks to the queue, I will only list one of them here. Let's just take a look at the underlying logic of learning it.

    //创建Timer,添加任务
    Timer t = new Timer;
    t.schedule(...)//此处参数省略了

    //...Timer类
    //参数一:任务,需要是Runnable接口的实现类;
    //参数二:触发时间
    //参数三:任务周期,也就是重复执行的时间间隔。与参数二都是以毫秒为单位
    public void schedule(TimerTask task, long delay, long period) {
        if (delay < 0)
            throw new IllegalArgumentException("Negative delay.");
        if (period <= 0)
            throw new IllegalArgumentException("Non-positive period.");
        sched(task, System.currentTimeMillis()+delay, -period);
    }

    //sched()方法
    //
    private void sched(TimerTask task, long time, long period) {
        if (time < 0)
            throw new IllegalArgumentException("Illegal execution time.");

        //充分约束周期值以防止数字溢出,同时仍然有效无限大。
        if (Math.abs(period) > (Long.MAX_VALUE >> 1))
            period >>= 1;

        synchronized(queue) {
            if (!thread.newTasksMayBeScheduled)
                throw new IllegalStateException("Timer already cancelled.");

            synchronized(task.lock) {
                if (task.state != TimerTask.VIRGIN)
                    throw new IllegalStateException(
                        "Task already scheduled or cancelled");
                task.nextExecutionTime = time;
                task.period = period;
                task.state = TimerTask.SCHEDULED; //这个常量设置了任务的状态:此任务计划执行。如果是非重复任务,则尚未执行
            }

            queue.add(task); //添加到队列中
            if (queue.getMin() == task) //取出第一个任务
                queue.notify(); //唤醒任务队列
        }
    }

    //将任务提升到优先级队列
    void add(TimerTask task) {
        //如有必要,增加后备存储
        if (size + 1 == queue.length)
            queue = Arrays.copyOf(queue, 2*queue.length);

        queue[++size] = task;
        fixUp(size);
    }


    //此方法的工作原理是反复“提升”队列 [k](通过将其与其父级交换),直到 queue[k] 的 nextExecutionTime 大于或等于其父级的 nextExecutionTime
    private void fixUp(int k) {
        while (k > 1) {
            int j = k >> 1;
            if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)
                break;
            //互换位置
            TimerTask tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;
            k = j;
        }
    }

The above is my personal learning experience on this java timer. Of course, there are still many things I haven't learned, and what I have learned is only a little bit of display. Please correct me if there are any deficiencies.

Guess you like

Origin blog.csdn.net/Ccc67ol/article/details/128392473