Java自己的Timer定时器

使用方法

public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("爱陈海娥");
            }
        }, 1000, 1000);
    }

如图所示java定时器使用还是很简单的,schedule方法接收三个参数,第一个参数 定时任务Job实现类、第二个参数是说这个job在延迟多少毫米后执行,第三个参数是说循环周期多少毫秒执行。

TimerTasker 任务Job类

这个类作为任务类首先需要考虑的就是线程安全问题, 比如一个TimerTasker实例 应该具有的是和定时任务有关的一些设计, 比如状态,下一次执行时间, 这个都是同一个实例是共享的, 所以我们加锁来保证不会出现并发修改问题, 最好的实践方法就是资源绑定锁对象,这样同一个实例搞一个锁,就可以实现多个线程公用一个锁对象

public abstract class TimerTask implements Runnable {
    /**
     * This object is used to control access to the TimerTask internals. 持有锁的对象
     */
    final Object lock = new Object();
   }

循环执行任务线程 TimerThread

要实现这个循环处理,是不能使用当前线程去做这件事情,要不然就阻塞了循环,所以需要开辟一个新的线程去做这件事, TimerThread 故继承了Thread, 即为实现多线程的一种方式。

public void run() {
        try {
            mainLoop(); //循环执行任务队列的jobTask
        } finally {
            // Someone killed this Thread, behave as if Timer cancelled
            synchronized(queue) { //获取任务队列的锁
                newTasksMayBeScheduled = false; //没有任务需要调度
                queue.clear();  // Eliminate obsolete references清空任务队列
            }
        }
    }

mainLoop

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())//队列为空 跳出循环终止, 注意来到这里说明newTasksMayBeScheduled为
                        break; // Queue is empty and will forever remain; die 注意来到这里说明newTasksMayBeScheduled为false

                    // 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)) {//当前时间是不是已经过了当前任务的执行时间
                            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);
                            }
                        }
                    }
                    if (!taskFired) // Task hasn't yet fired; wait 说明时间还没有到,睡剩余的时间
                        queue.wait(executionTime - currentTime);
                }
                if (taskFired)  // Task fired; run it, holding no locks 当前任务的执行时间已经到了
                    task.run(); //执行run方法,注意:这里是没有加锁的,所以我们的定时任务业务代码需要考虑线程安全问题
            } catch(InterruptedException e) {
            }
        }
    }
}

TimerThread的启动时机,在创建Timer对象的时候会启动调用TimerThread的start()方法

schedule 给Timer添加任务

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)//任务队列的最早执行任务是 当前task
                queue.notify(); //唤醒任务队列的线程
        }
    }

总结

TImer整体逻辑实现还是比较容易看明白的, 关键就是确定共享资源的范围已经如何加锁。