JAVA定时器使用与原理分析

如何使用

首先定义一个类,继承TimerTask

static class MyTimerTask extends TimerTask {
    
    

    private final MyWebSocketClient client;

    public MyTimerTask(MyWebSocketClient client) {
    
    
        this.client = client;
    }

    @Override
    public void run() {
    
    
        System.out.print("任务执行");
    }
}

配置MyTimerTask的执行策略

Timer timer = new Timer();
MyTimerTask timerTask = new MyTimerTask(client);
timer.schedule(timerTask, 1000, 2000);

上诉执行策略的意思为:从现在起过1000毫秒以后,每隔2000毫秒执行一次。
除了支持这种模式的策略设置还有很多

// time为Date类型:在指定时间执行一次。
timer.schedule(task, time);

// firstTime为Date类型,period为long
// 从firstTime时刻开始,每隔period毫秒执行一次。
timer.schedule(task, firstTime, period);

// delay 为long类型:从现在起过delay毫秒执行一次
timer.schedule(task, delay)

// delay为long,period为long:从现在起过delay毫秒以后,每隔period
// 毫秒执行一次。  
timer.schedule(task, delay, period)

原理分析

timer底层是把一个个任务放在一个TaskQueue中,TaskQueue是以平衡二进制堆表示的优先级队列,他是通过nextExecutionTime进行优先级排序的,距离下次执行时间越短优先级越高,通过getMin()获得queue[1]

并且出队的时候通过synchronized保证线程安全,延迟执行和特定时间执行的底层实现类似,源码如下

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

    // Constrain value of period sufficiently to prevent numeric
    // overflow while still being effectively infinitely large.
    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();
    }
}

我们主要来看下周期性调度通过什么方式实现的,我们直接来分析源码如下:

private void mainLoop() {
    
    
    // 首先一直监听队列中有没有任务
    while (true) {
    
    
        try {
    
    
            TimerTask task;
            boolean taskFired;
            // 同步,保证任务执行顺序
            synchronized (queue) {
    
    
                // Wait for queue to become non-empty
                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; // No action required, poll queue again
                    }
                    currentTime = System.currentTimeMillis();
                    // 获取任务下次执行时间
                    executionTime = task.nextExecutionTime;
                    if (taskFired = (executionTime <= currentTime)) {
    
    
                        // 到这里是延迟执行和特定时间点执行已经结束了,状态标记为EXECUTED,周期性执行继续往下走
                        if (task.period == 0) {
    
     // Non-repeating, remove
                            queue.removeMin();
                            task.state = TimerTask.EXECUTED;
                        } else {
    
     // Repeating task, reschedule
                            // 这里他又重新计算了下下个任务的执行,并且任务还在队列中
                            queue.rescheduleMin(
                                    task.period < 0 ? currentTime - task.period
                                            : executionTime + task.period);
                        }
                    }
                }
                // 如果任务执行时间大于当前时间说明任务还没点,继续等,否则执行run代码块
                if (!taskFired) // Task hasn't yet fired; wait
                    queue.wait(executionTime - currentTime);
            }
            if (taskFired) // Task fired; run it, holding no locks
                task.run();
        } catch (InterruptedException e) {
    
    
        }
    }
}
}

缺点
1、首先Timer对调度的支持是基于绝对时间的,而不是相对时间,所以它对系统时间的改变非常敏感。

2、其次Timer线程是不会捕获异常的,如果TimerTask抛出的了未检查异常则会导致Timer线程终止,同时Timer也不会重新恢复线程的执行,他会错误的认为整个Timer线程都会取消。同时,已经被安排单尚未执行的TimerTask也不会再执行了,新的任务也不能被调度。故如果TimerTask抛出未检查的异常,Timer将会产生无法预料的行为

3、Timer在执行定时任务时只会创建一个线程任务,如果存在多个线程,若其中某个线程因为某种原因而导致线程任务执行时间过长,超过了两个任务的间隔时间,会导致下一个任务执行时间滞后

这些缺点可以通过ScheduledExecutorService来代替

参考:https://blog.csdn.net/fuyuwei2015/article/details/83825851

猜你喜欢

转载自blog.csdn.net/qq32933432/article/details/127052195