java 多线程 Timer类

Timer类      

        需要处理按计划时间执行的任务时,可使用Timer类。
任务TimerTask
        创建一个Timer类的对象后,Timer对象的内部会启动一个线程TimerThread,它只有一个线程来管理任务。
抽象类TimerTask实现了Runnable接口。 任务类继承TimerTask类,重写的run方法体就是要执行的任务。将任务对象提交给Timer对象,就可以执行线程了。


任务调度原理
              Timer文件下有三个类,Timer类,Timer子类TimerThread继承Thread类,是个线程,TaskQueue二叉堆,Timer类创建了TaskQueue类的queue对象,将其作为参数传给TimerThread创建了thread对象
             所有加入Timer的任务都会通过sched方法放入到TaskQueue中(TaskQueue的add方法)【1】。(调度任务) TimerThread线程的run方法体中mainloop循环方法会不断尝试执行TaskQueue中的任务【2】,执行完所有任务【3】该线程才会结束。

【1】TaskQueue的add方法调用时,先判断堆容量是否够用,不够就翻倍扩容,将任务放到最后,再调用fixUp对任务的优先级排序,TimerTask按nextExecutionTime【4】进行堆排序。每次取堆中一个nextExecutionTime和当前系统时间进行比较,如果当前时间大于nextExecutionTime就会执行,如果是一次性任务,执行完了会将其从堆中移除。否则更新其nextExecutionTime的值。明显地,nextExecutionTime最小的任务在queue[1],要被最先执行.
 

   【2】这里仅分析下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())
                        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)) {
                            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();
            } catch(InterruptedException e) {
            }
        }
    }

          首先判断堆中任务为空且newTasksMayBeScheduled为true【6】,如果是,进入while循环体等待新任务到来,如果不是,取出第一个任务,判断是否被取消,如被取消,从任务队列移除该任务,进入下个调度周期,如未被取消,判断是否到达执行时间,如未到,则等待差的这段时间,如果到了,先进行下次调度的准备工作(判断period是否为0,如果为0,将这个任务从任务列表移除,如果不为0,就根据period>0或者period<0调用TaskQueue的rescheduleMin方法(重新设置nextExecution,再调用fixDown方法排序)),再执行run方法,即任务。

  【3】执行完所有任务的意思是,对单次的任务,执行完,对周期性重复的任务,取消它

【4】long nextExecutionTime 记录该任务下次执行时间

【5】long period 描述任务的执行方式: 0表示只执行一次任务. 正数表示固定速率执行的任务. 从设定的开始时间起,每period时间执行一次。负数表示固定延迟执行的任务. 上次执行该任务的period时间后再次执行该任务。

【6】boolean  newTasksMayBeScheduled表示是否继续等待新任务,默认设为true,通过查看代码,发现当调用cancel方法或者杀死这个进程,没有引用指向这个对象时,该变量被置为false


下面接着分析类方法:

Timer类

构造方法
Timer() 创建一个计时器对象。
Timer(boolean isDaemon) 创建一个计时器对象,设置其是否为守护线程。

Timer(String name) ,创建一个计时器对象,指定相关线程的名字。
Timer(Stringname, boolean isDaemon)
创建计时器对象,设置相关线程名字和是否为守护线程。
常用方法
void schedule(TimerTasktask,Date time)在指定时间执行某个任务。
void schedule(TimerTasktask,Date firstTime, long period)从指定的时间开始进行重复的以固定间隔时间执行某个任务。
void schedule(TimerTasktask, long delay)在指定延迟时间后执行某个任务。
void schedule(TimerTasktask, long delay, long period)从指定的延迟时间后开始进行重复地以固定间隔时间执行某个任务。
void scheduleAtFixedRate(TimerTasktask,Date firstTime, long period) 从指定时间开始,以固定频率周期性地执行某个任务
void scheduleAtFixedRate(TimerTasktask, long delay, long period)从指定延时开始,以固定频率周期性地执行某个任务。

void cancel() 取消所有已安排正在等待的任务,该定时器相关线程死亡。
int purge() 从当前计时器的任务队列(task queue)中移除所有已取消的任务,将其引用置为null,方便jvm回收内存。对比TimerTask中 cancel方法,一次只能取消一个任务。
schedule和scheduleAtFixedRate的区别
如果设定的指定时间已经过去了才开始运行程序,schedule会将当前时间当做指定时间来开始周期性地执行任务,而scheduleAtFixedRate还是以设定的指定时间作为起始时间,先将因为迟到欠下的任务补上,再按照原来的设定接着执行。如果任务比较复杂,或者其他原因,如回收垃圾,大量线程在等待执行等因素导致延迟了某次的执行,schedule按固定时间间隔调度,每次的延迟传播到后续的任务中,scheduleAtFixedRate按照固定比率调度,先执行多次将欠下的任务补回来,再按近似的固定频率执行,长时间来看,scheduleAtFixedRate的执行频率较稳定.


TimerTask类
Timer中的任务,在Timer对象中可安排一次执行或周期性执行的任务
public abstract class TimerTask  implements Runnable 抽象方法,继承Runnable
构造方法:
protected TimerTask()  创建计时器任务对象。
常用方法:
boolean cancel() 取消此计时器任务。 一次只能取消一个
abstract void run()计时器任务,继承TimerTask后要重写该抽象方法。
long scheduledExecutionTime()返回该任务最近一次安排的执行时间。
 

使用示例:

每隔30s打印依次helloworld

package chen_chapter_9;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class TimerTest01 {
	public static void main(String[] args) {
		// 创建任务对象
		Work1 work1 = new Work1();
		// 创建定时器对象
		Timer timer = new Timer();

		// 创建SimpleDateFormat对象
		SimpleDateFormat sdf = new SimpleDateFormat();
		//设置日期时间的格式
		sdf.applyPattern("yyyy-MM-dd hh:mm:ss");
		// SimpleDateFormat对象的格式要和解析的格式一样,否则报错
		
		Date date = null;
		try {
			date = sdf.parse("2018-11-26 10:21:00");
			System.out.println(date);//打印时间
		} catch (ParseException e) {

			e.printStackTrace();
		}
		//安排任务
		timer.schedule(work1, date, 30000);

	}
}

class Work1 extends TimerTask {

	@Override
	public void run() {

		System.out.println("hello  world!");
	}

}
out:
Mon Nov 26 10:21:00 CST 2018
hello  world!
hello  world!
hello  world!

参考 https://blog.csdn.net/shengsummer/article/details/44460179

        https://www.cnblogs.com/xiaotaoqi/p/6874713.html

猜你喜欢

转载自blog.csdn.net/sinat_41132860/article/details/84503464
今日推荐