Java多线程总结(3)— Timer 和 TimerTask深入分析
来源:互联网 发布:华为软件商城下载 编辑:程序博客网 时间:2024/06/04 17:40
1. 基本概念
java.util.Timer:是一个实用工具类,该类用来调度一个线程(schedule a thread),使它可以在将来某一时刻执行。 Java的Timer类可以调度一个任务运行一次,或定期循环运行。 Timer tasks should complete quickly. 即定时器中的操作要尽可能花费短的时间。
java.util.TimerTask:是一个抽象类,它实现了Runnable接口。我们需要扩展该类以便创建自己的TimerTask,这个TimerTask可以被Timer调度。
注意:默认, task执行线程不是daemon线程, 任务执行完,主线程(或其他启动定时器的线程)结束时,task线程并没有结束。如果调用者想要快速终止计时器的任务执行线程,调用者应该调用timer.cancel()
方法。
public void timerTest() { Timer myTimer = new Timer(); myTimer.schedule(new TimerTask() { @Override public void run() { System.out.println("1s后运行"); myTimer.cancel(); // 需要手动cancel } }, 1000);}
2. 源码解析及案例
Timer类是线程安全的,多进程不需要外部同步机制就可以共享同一个Timer对象。创建Timer对象,会创建一个java.util.TaskQueue实例,在执行定时任务时,将taskqueue对象作为锁,在指定时间间隔添加任务,在任何时刻只能有一个线程执行TimerTask。
Timer类使用对象的wait和notify方法来调度任务。
查看Timer源代码:
// TaskQueue队列,内部就是一个TimerTask[]数组private final TaskQueue queue = new TaskQueue();// Timer内部维持一个叫TimerThread的线程,传递TaskQueue队列private final TimerThread thread = new TimerThread(queue);// 创建Timer即启动线程public Timer(String name) { thread.setName(name); thread.start();// 启动线程,后面有分析TimerThread的run方法}public void schedule(TimerTask task, long delay) { if (delay < 0) throw new IllegalArgumentException("Negative delay."); sched(task, System.currentTimeMillis()+delay, 0);}// 核心调度方法,time表示执行的绝对时间private void sched(TimerTask task, long time, long period) { ... // timer对象中的queue作为锁 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为调度状态 task.state = TimerTask.SCHEDULED; } // 将当前待执行的task添加到队列中 queue.add(task); // 队列中取出的head task为当前task if (queue.getMin() == task) // 在任何时刻只能有一个线程执行TimerTask queue.notify(); }}
其中Timer中启动的TimerThread的run方法:
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 } }}private void mainLoop() { while (true) { try { TimerTask task; boolean taskFired; synchronized(queue) { // 如果queue队列为空,则将线程阻塞,等待task 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 task = queue.getMin(); synchronized(task.lock) { // 此处说明可以通过cancel()设置终止task的执行,但TimerThread并没有终止 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(); // 执行task的run方法! } catch(InterruptedException e) { } }}
TimerTask的调度流程:由以上分析可知,当我们创建一个Timer时,同时内部创建了一个TimerThread线程,并启动它,该线程会不断扫描从Timer传递进来的task队列,如果为空,则wait()阻塞该线程;当timer调用shedule方法的时候,将传递的task添加到队列中,同时调用queue.notify()
方法唤醒TimerThread线程,则从队列中取出task根据给定的等待时间wait等待,等待完成后执行task.run();
启动任务。(这种结构可以应用到简单的爬虫中)
如何终止Timer线程:
默认情况下,创建的timer线程会一直执行,一般通过下面的方式来终止timer线程:
- 调用timer的cancle方法
- 把timer线程设置成daemon线程,(new Timer(true)创建daemon线程),在jvm里,如果所有用户线程结束,那么守护线程也会被终止,不过这种方法一般不用。
下面给出一个Timer执行多个task的简单案例:
public void multileTasksShareOneTimer() { Timer myTimer = new Timer(); TimerTask task1 = new TimerTask() { @Override public void run() { System.out.println(Thread.currentThread().getName() +" 1s后执行task1"); //myTimer.cancel(); } }; TimerTask task2 = new TimerTask() { @Override public void run() { System.out.println(Thread.currentThread().getName() +" 2s后执行task2"); //myTimer.cancel(); } }; myTimer.schedule(task1, 1000); // task2.cancel(); myTimer.schedule(task2, 2000);}
运行输出:注意此时Timer的TimerThread并没有结束,因为在mainLoop()等待task而wait进入阻塞状态!
如果设置了task2.cancel();则调度执行task2会抛出异常:
定时器实际使用的场景还是很多的,比如下面的,在每天零晨备份数据库,等等。
/** * 每天0晨备份数据库 */@SuppressWarnings("deprecation")public void backupDatabase() { Timer timer = new Timer(); TimerTask task = new TimerTask() { @Override public void run() { // 备份数据库 System.out.println("数据库备份..."); // other operation } }; Date firstTime = new Date(); firstTime.setHours(0); firstTime.setMinutes(0); firstTime.setSeconds(0); long oneDayPeriod = 24 * 60 * 60 * 1000; timer.scheduleAtFixedRate(task, firstTime, oneDayPeriod); // timer.schedule(task, firstTime, oneDayPeriod);}
最后:如果是简单的定时调度,使用Timer就够了,如果复杂的调度任务,可以考虑使用Quartz
转载请注明出处:Java多线程总结(3)— Timer 和 TimerTask深入分析
- Java多线程总结(3)— Timer 和 TimerTask深入分析
- Timer & TimerTask 深入分析
- 定时器之Timer和TimerTask深入分析
- Java线程(3)Timer和TimerTask
- Java 中 Timer 和 TimerTask实现多线程
- Java多线程(五):Timer和TimerTask
- Timer 和TimerTask分析
- Java多线程编程--(2)Timer & TimerTask
- Java多线程编程--(2)Timer & TimerTask
- Timer和TimerTask总结
- Java Timer和TimerTask
- JAVA Timer和TimerTask
- Java Timer和TimerTask
- java Timer和TimerTask
- Java Timer和Timertask
- 【Java多线程】-Timer,TimerTask,ScheduledExecutorService
- Java—Timer和TimerTask详解(常用API)
- Java线程(五)----Timer和TimerTask
- 通过反射和class文件asm字节码分析方法是get或者set方法
- leetcode:172 Factorial Trailing Zeroes-每日编程第二十四题
- Hadoop文件系统元数据的持久化
- 泛型委托,即参数的类型不确定,以达到更高的灵活性:
- codeforces 424A Squats
- Java多线程总结(3)— Timer 和 TimerTask深入分析
- FreeImage使用方法
- 杭电ACM1850(Nim博弈)
- Bootstrap的安装与配置
- 【POJ 2104】K-th Number 题意&题解&代码(c++)
- Image
- 集合的交、并、差运算
- -[__NSCFNumber rangeOfCharacterFromSet:]: unrecognized selector sent to instance
- codeforces 424B Megacity