Timer解析

Timer原理解析

jdk版本:1.8

一.使用场景

Timer可以用来触发或执行定时性任务,周期性任务,或者两者同时兼有。
为此Timer提供了 以下API:

        Timer timer = new Timer();
        TimerTask task = new TimerTask() {
            @SneakyThrows
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getId() + "===>" + System.currentTimeMillis());
                Thread.sleep(1000);
            }
        };
        Date date = new Date(System.currentTimeMillis() + 1000);
        
        //1.在date时间点执行task任务
        timer.schedule(task, date);
        
        //2.在date时间点执行task任务,此后每过1秒执行一次
        // 下面的scheduleAtFixedRate实际是调用了schedule
        timer.schedule(task, date, 1000);
        timer.scheduleAtFixedRate(task,date,1000);

        //3.延时触发,2秒后执行task任务
        timer.schedule(task,2000);

        //4.延时触发,1秒后执行task任务,此后每过2秒执行一次,
        // 下面的scheduleAtFixedRate实际是调用了schedule
        timer.schedule(task,1000,2000);
        timer.scheduleAtFixedRate(task, 1000, 2000);

二.运行原理

Timer的两个重要的属性:

  1. TaskQueue queue:用于存放TimerTask的一个优先级队列(拥有最小nextExecutionTime的TimerTask会被放到queue[1]);
  2. TimerThread thread:Timer运行方式的是单线程,这里的单线程指的就是这个thread,thread在一个while (true)循环里,获取并处理queue里面的TimerTask。

Timer的重要步骤:
1.Timer timer = new Timer()
这一步创建了Timer,同时启动了维护Timer运行的线程thread,在thread的run方法里有一个最重要的mainLoop()方法,Timer就是使用该方法一直循环监控并处理queue里面的任务:

  private void mainLoop() {
        while (true) {
            try {
                TimerTask task;
                boolean taskFired;
                synchronized(queue) {
                    // 判断装TimerTask的queue是否为空,为空则进入等待
                    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;
                    //queue非空就获取queue[1]的TimerTask,queue[1]拥有最小的nextExecutionTime值
                    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)) {
                            if (task.period == 0) { 
                            //不许重复执行的任务,从queue拿出来后,就删除,避免重复执行
                                queue.removeMin();
                                task.state = TimerTask.EXECUTED;
                            } else { 
                            //需要重复执行的任务,则重置queue[1]的nextExecutionTime为currentTime + |task.period|
                            //并根据 nextExecutionTime对queue进行升序排列
                                queue.rescheduleMin(
                                  task.period<0 ? currentTime   - task.period
                                                : executionTime + task.period);
                            }
                        }
                    }
                    if (!taskFired) // Task hasn't yet fired; wait
                        queue.wait(executionTime - currentTime);
                }
                if (taskFired)  // Task fired; run it, holding no locks
                //执行TimerTask任务
                    task.run();
            } catch(InterruptedException e) {
            }
        }
    }

2.timer.schedule(task, xxxxxxxxxxx)
这一步是将TimerTask放入queue中,这样thread在mainLoop循环循环中才可以取到数据并处理,源码如下(以schedule(TimerTask task, long delay, long period)为例):

 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");
                //封装TimerTask
                task.nextExecutionTime = time;
                task.period = period;
                task.state = TimerTask.SCHEDULED;
            }
            //这里最终要的就是这一步:将TimerTask放入queue中
            queue.add(task);
            if (queue.getMin() == task;
            //唤醒mainLoop方法里wait状态中的queue,让mainloop方法继续执行下去
                queue.notify();
        }
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值