天天看點

Java定時任務排程之Timer

定時任務定義

    定時任務排程:基于給定的時間點、給定的時間間隔或者給定的執行次數自動執行的任務。

JDK定時任務Timer

1.JDK定時任務工具類Timer

    Timer:有且僅有一個背景線程對多個業務線程進行定時、定頻率的排程;Timer定時任務(schedule)的四種用法:背景線程Timer——業務線程TimerTask

  • schedule(task,time)——在時間等于或者超過time的時候執行且僅執行一次task;(schedule1代碼)
  • schedule(task,time,period)——時間等于或者超過timer時首次執行task,之後每隔period毫秒重複執行一次task;(schedule2代碼)
  • schedule(task,delay)——等待delay毫秒後執行且僅執行一次task;(schedule3代碼)
  • schedule(task,delay,period)——等待delay毫秒後首次執行task,之後每隔period毫秒重複執行一次task;(schedule4代碼)
  • scheduleAtFixedRate(task,time,period)——時間等于或者超過time時首次執行task,之後每隔period毫秒重複執行一次task;(atFixedRate1代碼)
  • scheduleAtFixedRate(task,delay,period)——等待delay毫秒後首次執行task,之後每隔period毫秒重複執行一次task;(atFixedRate2代碼)

2.建立TimerTask業務線程實作類JdkTimerTask:

package com.luna.timer.jdk;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimerTask;
public class JdkTimerTask extends TimerTask{
	private String name;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}	
	public JdkTimerTask() {
	}
	@Override
	public void run() {
		Calendar calendar = Calendar.getInstance();
		SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		System.out.println("Current exec time is:"+sf.format(calendar.getTime()));
		System.out.println("Current exec name is:"+name);
	}

}
           

3.建立背景線程Timer類的實作JDKTimer:

package com.luna.timer.jdk;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;
public class JdkTimer {
	public static void main(String[] args) {
		Timer timer = new Timer();
		JdkTimerTask timerTask = new JdkTimerTask();
		Calendar calendar = Calendar.getInstance();
		SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		System.out.println("Current exec time is:"+sf.format(calendar.getTime()));
		//擷取目前時間3秒之後的時間,各個任務獨立運作避免互相影響
		calendar.add(Calendar.SECOND, 3);
		
//		timerTask.setName("schedule1");
//		//在時間等于或者超過time的時候執行且僅執行一次task
//		timer.schedule(timerTask, calendar.getTime());
		
//		timerTask.setName("schedule2");
//		//時間等于或者超過timer時首次執行task,之後每隔period毫秒重複執行一次task
//		timer.schedule(timerTask, calendar.getTime(),2000);
		
//		timerTask.setName("schedule3");
//		//等待delay毫秒後執行且僅執行一次task
//		timer.schedule(timerTask, 1000);
//		System.out.println("最近發生此任務執行安排的時間為:"+sf.format(timerTask.scheduledExecutionTime()));
		
//		timerTask.setName("schedule4");
//		//等待delay毫秒後首次執行task,之後每隔period毫秒重複執行一次task
//		timer.schedule(timerTask, 3000, 2000);
		
//		timerTask.setName("atFixedRate1");
//		//時間等于或者超過time時首次執行task,之後每隔period毫秒重複執行一次task
//		timer.scheduleAtFixedRate(timerTask, calendar.getTime(),2000);
		
		timerTask.setName("atFixedRate2");
		//等待delay毫秒後首次執行task,之後每隔period毫秒重複執行一次task
		timer.scheduleAtFixedRate(timerTask, 3000,2000);
	}
}
           

4.Timer其他函數

  • TimerTask類cancel()——取消目前TimerTask裡的循環執行的任務;
package com.luna.timer.jdk;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimerTask;
public class JdkCancelTimerTask extends TimerTask{
	private String name;
	private Integer count = 0;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}	
	public JdkCancelTimerTask() {
	}
	@Override
	public void run() {
		if(count<3){
			Calendar calendar = Calendar.getInstance();
			SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			System.out.println("Current exec time is:"+sf.format(calendar.getTime()));
			System.out.println("Current exec name is:"+name);
			count++;
		}else{
			cancel();
			System.out.println("Task cancel!");
		}
	}
}
           
  • TimerTask類scheduleExecutionTime()——傳回此任務最近實際執行的已安排執行的時間;傳回值為最近發生此任務執行安排的時間(Long);
  • Timer類cancel()——終止此計時器,丢棄所有目前已安排的任務;
package com.luna.timer.jdk;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
public class JdkCancelTimer {
	public static void main(String[] args) throws InterruptedException {
		Timer timer = new Timer();
		JdkTimerTask timerTask1 = new JdkTimerTask();
		timerTask1.setName("timerTask1");
		JdkTimerTask timerTask2 = new JdkTimerTask();
		timerTask2.setName("timerTask2");
		
		Date startTime = new Date();
		SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		System.out.println("Start time is:"+sf.format(startTime));
		
		timer.schedule(timerTask1, 3000,2000);
		timer.schedule(timerTask2, 1000,2000);
		
		Thread.sleep(5000);
		Date cancelTime = new Date();
		System.out.println("Cancel time is:"+sf.format(cancelTime));
		
		timer.cancel();
		System.out.println("Tasks all canceled!");
	}
}
           
  • purge()——從此計時器的任務隊列中移除所有已取消的任務,傳回值為隊列中移除的任務數;
package com.luna.timer.jdk;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
public class JdkPurgeTimer {
	public static void main(String[] args) throws InterruptedException {
		Timer timer = new Timer();
		JdkTimerTask timerTask1 = new JdkTimerTask();
		timerTask1.setName("timerTask1");
		JdkTimerTask timerTask2 = new JdkTimerTask();
		timerTask2.setName("timerTask2");
		
		Date startTime = new Date();
		SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		System.out.println("Start time is:"+sf.format(startTime));
		
		timer.schedule(timerTask1, 3000,2000);
		timer.schedule(timerTask2, 1000,2000);
		System.out.println("目前已取消的任務數為:"+timer.purge());
		
		Thread.sleep(2000);
		Date cancelTime = new Date();
		System.out.println("Cancel time is:"+sf.format(cancelTime));
		
		timerTask2.cancel();
		System.out.println("目前已取消的任務數為:"+timer.purge());
	}
}
           

5.schedule與scheduleAtFixedRate的差別

  • 首次計劃執行的時間早于目前的時間
  • "fixed-delay"——如果第一次執行時間被delay了,随後的執行時間按照上一次實際執行完成的時間進行計算;
package com.luna.timer.jdk;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
public class DifferTest {
	public static void main(String[] args) {
		final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Calendar calendar = Calendar.getInstance();
		System.out.println("目前時間為:"+sf.format(calendar.getTime()));
		calendar.add(Calendar.SECOND, -6);
		Timer timer = new Timer();
		//第一次執行時間為6秒前,之後每隔兩秒鐘執行一次
		timer.schedule(new TimerTask() {
			@Override
			public void run() {
				System.out.println("目前計劃執行時間為:"+sf.format(scheduledExecutionTime()));
				System.out.println("定時任務正在被執行!");
			}
		},calendar.getTime(), 2000);
	}
}
           
  • "fixed-rate"——如果第一次執行時間被delay了,随後的執行時間按照上一次開始的時間點進行計算并且為了趕上進度會多次執行任務,是以TimerTask中的執行體需要考慮同步;
package com.luna.timer.jdk;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
public class DifferTest {
	public static void main(String[] args) {
		final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Calendar calendar = Calendar.getInstance();
		System.out.println("目前時間為:"+sf.format(calendar.getTime()));
		calendar.add(Calendar.SECOND, -6);
		Timer timer = new Timer();
		//第一次執行時間為6秒前,之後每隔兩秒鐘執行一次
		timer.scheduleAtFixedRate(new TimerTask() {
			@Override
			public void run() {
				System.out.println("目前計劃執行時間為:"+sf.format(scheduledExecutionTime()));
				System.out.println("定時任務正在被執行!");
			}
		},calendar.getTime(), 2000);
	}
}
           
  • 任務執行所需時間超出任務的執行周期間隔
  • schedule()——下一次執行時間相對于上一次實際執行完成的時間點,是以執行時間會不斷延後;
package com.luna.timer.jdk;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
public class DifferTest {
	public static void main(String[] args) {
		final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Calendar calendar = Calendar.getInstance();
		System.out.println("目前時間為:"+sf.format(calendar.getTime()));
		Timer timer = new Timer();
		//第一次執行時間為6秒前,之後每隔兩秒鐘執行一次
		timer.schedule(new TimerTask() {
			@Override
			public void run() {
				try {
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("目前計劃執行時間為:"+sf.format(scheduledExecutionTime()));
				System.out.println("定時任務正在被執行!");
			}
		},calendar.getTime(), 2000);
	}
}
           
  • scheduleAtFixedRate()——下一次執行時間相對于上一次開始的時間點,是以執行時間一般不會延後,是以存在并發性;
package com.luna.timer.jdk;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
public class DifferTest {
	public static void main(String[] args) {
		final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Calendar calendar = Calendar.getInstance();
		System.out.println("目前時間為:"+sf.format(calendar.getTime()));
		Timer timer = new Timer();
		//第一次執行時間為6秒前,之後每隔兩秒鐘執行一次
		timer.scheduleAtFixedRate(new TimerTask() {
			@Override
			public void run() {
				try {
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("目前計劃執行時間為:"+sf.format(scheduledExecutionTime()));
				System.out.println("定時任務正在被執行!");
			}
		},calendar.getTime(), 2000);
	}
}
           

5.灌水機器人和跳舞機器人

      灌水機器人每隔1秒灌一次水到木桶中,跳舞機器人每隔兩秒跳一次舞,當木桶水滿了時(水容量達到5L)灌水機器人停止灌水,2秒後跳舞機器人停止跳舞。灌水機器人代碼如下:

package com.luna.timer.jdk.robot;
import java.util.Timer;
import java.util.TimerTask;
public class WaterRobot extends TimerTask{
	private Timer timer;	
	public WaterRobot(Timer timer) {
		this.timer = timer;
	}	
	//最大容量為5L
	private Integer bucketCapacity = 0;
	@Override
	public void run() {
		//灌水直至桶滿為止
		if(bucketCapacity<5){
			System.out.println("灌水機器人灌水一升到木桶中!");
			bucketCapacity++;
		}else{
			//水滿之後就停止執行
			cancel();
			System.out.println("灌水機器人停止執行!");
			System.out.println("取消的任務數目為:"+timer.purge());
			System.out.println("目前桶中的水容量為:"+bucketCapacity+"L");
			try {
				//等待兩秒終止timer裡面的所有内容
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			timer.cancel();
		}
	}
}
           

跳舞機器人代碼如下:

package com.luna.timer.jdk.robot;
import java.text.SimpleDateFormat;
import java.util.TimerTask;
public class DancingRobot extends TimerTask{
	@Override
	public void run() {
		SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		System.out.println("計劃任務執行時間是:"+sf.format(scheduledExecutionTime()));
		System.out.println("跳舞機器人在愉快的跳舞。。。");
	}
}
           

測試代碼如下:

package com.luna.timer.jdk.robot;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;
public class Executor {
	public static void main(String[] args) {
		Timer timer = new Timer();
		Calendar calendar = Calendar.getInstance();
		SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		System.out.println("計劃任務執行時間是:"+sf.format(calendar.getTime()));
		DancingRobot dancingRobot = new DancingRobot();
		WaterRobot waterRobot = new WaterRobot(timer);
		
		timer.schedule(dancingRobot, calendar.getTime(),2000);
		timer.scheduleAtFixedRate(waterRobot, calendar.getTime(),1000);
	}
}
           

定時任務Timer總結

  • 管理并發任務的缺陷:Timer有且僅有一個線程去執行定時任務,如果存在多個任務,且任務時間過長,會導緻執行效果與預期不符;
  • 如果TimerTask抛出RuntimeException,Timer會停止所有任務的運作;
  • Timer不支援對時效性要求較高的多任務并發作業,也不支援複雜的任務排程(可能有異常、每周三執行一次任務等場景)那麼大哥Quartz就要出馬了。