定時器Timer的使用
Time類的主要作用就是設置計劃任務,但封裝任務的類卻是TimeTask類,執行計劃任務的代碼要放入TimerTask的子類中,因爲TimeTask是一個抽象類。
1.方法schedule(TimeTask task,Date Time)的測試
該方法的作用是在指定的日期執行一次某一任務。
1.1 執行任務的時間晚於當前時間:在未來執行的效果
創建Run1.java類:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.SortedSet;
import java.util.Timer;
import java.util.TimerTask;
public class Run1 {
public static Timer timer = new Timer();
static public class MyTask extends TimerTask{
@Override
public void run() {
System.out.println("運行了!時間爲:"+new Date());
}
}
public static void main(String[] args) {
try {
MyTask task = new MyTask();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2020-06-29 15:10:00";
Date dateRef = sdf.parse(dateString);
System.out.println("字符串時間:"+dateRef+"當前時間:"+new Date());
timer.schedule(task,dateRef);
}catch (ParseException e){
e.printStackTrace();
}
}
}
最後的運行結果爲:
字符串時間:Mon Jun 29 15:10:00 CST 2020當前時間:Mon Jun 29 15:08:48 CST 2020
運行了!時間爲:Mon Jun 29 15:10:00 CST 2020
任務雖然執行完畢,但是能看到進程還未銷燬,呈紅色狀態。
實際上,創建一個Timer就是啓動一個新的線程,這個新啓動的線程並不是守護線程,它一直在運行。
所以解決這個辦法,就是在創建Timer之後將他設值成一個守護線程
public class Run1 {
public static Timer timer = new Timer(true);
static public class MyTask extends TimerTask{
@Override
public void run() {
System.out.println("運行了!時間爲:"+new Date());
}
}
public static void main(String[] args) {
try {
MyTask task = new MyTask();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2020-06-29 15:10:00";
Date dateRef = sdf.parse(dateString);
System.out.println("字符串時間:"+dateRef+"當前時間:"+new Date());
timer.schedule(task,dateRef);
}catch (ParseException e){
e.printStackTrace();
}
}
}
1.2 執行任務的時間早於當前時間:則立即執行
1.3 多個TimerTask及延時的測試
Timer允許有多個TimerTask任務。
創建新的Run2類:
public class Run2 {
public static Timer timer = new Timer();
static public class MyTask1 extends TimerTask{
@Override
public void run() {
System.out.println("task1運行了!時間爲:"+new Date());
}
}
static public class MyTask2 extends TimerTask{
@Override
public void run() {
System.out.println("task2運行了!時間爲:"+new Date());
}
}
public static void main(String[] args) {
try {
MyTask1 task = new MyTask1();
MyTask2 task2 = new MyTask2();
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString1 = "2020-06-29 15:31:00";
String dateString2 = "2020-06-29 15:31:00";
Date dateRef1 = sdf1.parse(dateString1);
Date dateRef2 = sdf2.parse(dateString2);
System.out.println("字符串1時間:"+dateRef1+"當前時間:"+new Date());
System.out.println("字符串2時間:"+dateRef2+"當前時間:"+new Date());
timer.schedule(task,dateRef1);
timer.schedule(task2,dateRef2);
}catch (ParseException e){
e.printStackTrace();
}
}
}
運行結果爲:
字符串1時間:Mon Jun 29 15:31:00 CST 2020當前時間:Mon Jun 29 15:31:06 CST 2020
字符串2時間:Mon Jun 29 15:31:00 CST 2020當前時間:Mon Jun 29 15:31:06 CST 2020
task1運行了!時間爲:Mon Jun 29 15:31:06 CST 2020
task2運行了!時間爲:Mon Jun 29 15:31:06 CST 2020
TimerTask是以隊列的方式一個一個被順序執行的,所以執行的時間有可能和預期的時間不一致,因爲前面的任務有可能耗時時間較長,則後面的任務運行的時間也會被延遲。
2.方法schedule(TimerTask task, Date firstTime,long period)的測試
該方法的作用是在指定的日期之後,按指定的間隔週期性地無限循環地執行某一任務。
2.1 計劃時間晚於當前時間:在未來執行的效果
修改Run1.java類如下:
public class Run1 {
public static Timer timer = new Timer();
static public class MyTask extends TimerTask{
@Override
public void run() {
System.out.println("運行了!時間爲:"+new Date());
}
}
public static void main(String[] args) {
try {
MyTask task = new MyTask();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2020-06-29 15:50:00";
Date dateRef = sdf.parse(dateString);
System.out.println("字符串時間:"+dateRef+"當前時間:"+new Date());
timer.schedule(task,dateRef,4000);
}catch (ParseException e){
e.printStackTrace();
}
}
}
運行結果如下:從運行結果來看,每隔4秒執行一次task任務,並且是無限期的重複執行。
字符串時間:Mon Jun 29 15:50:00 CST 2020當前時間:Mon Jun 29 15:50:17 CST 2020
運行了!時間爲:Mon Jun 29 15:50:17 CST 2020
運行了!時間爲:Mon Jun 29 15:50:21 CST 2020
運行了!時間爲:Mon Jun 29 15:50:25 CST 2020
2.2 計劃時間早於當前時間:立即執行
2.3 任務執行時間同樣會有被延時的可能
3. TimerTask的Cancel方法
TimerTask類中的cancel()方法的作用是將自身從任務隊列中清除。
新建Run3.java:
public class Run3 {
public static Timer timer = new Timer();
static public class MyTaskA extends TimerTask{
@Override
public void run() {
System.out.println("A運行了!時間爲:"+new Date());
this.cancel();//取消自身
}
}
static public class MyTaskB extends TimerTask{
@Override
public void run() {
System.out.println("B運行了!時間爲:"+new Date());
}
}
public static void main(String[] args) {
try {
MyTaskA task = new MyTaskA();
MyTaskB task2 = new MyTaskB();
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString1 = "2020-06-29 15:56:00";
Date dateRef2 = sdf1.parse(dateString1);
System.out.println("字符串2時間:"+dateRef2+"當前時間:"+new Date());
timer.schedule(task,dateRef2,4000);
timer.schedule(task2,dateRef2,4000);
}catch (ParseException e){
e.printStackTrace();
}
}
}
最後的運行結果如下:A在之後的循環執行中不再執行
字符串2時間:Mon Jun 29 15:56:00 CST 2020當前時間:Mon Jun 29 15:56:51 CST 2020
A運行了!時間爲:Mon Jun 29 15:56:51 CST 2020
B運行了!時間爲:Mon Jun 29 15:56:51 CST 2020
B運行了!時間爲:Mon Jun 29 15:56:55 CST 2020
4.Timer類的cancel()方法
Timer類的cancel()和TimerTask的cancel()不同,Timer類中的cancel()的作用是將任務隊列中的全部任務清空。
修改Run3.java如下:
public class Run3 {
public static Timer timer = new Timer();
static public class MyTaskA extends TimerTask{
@Override
public void run() {
System.out.println("A運行了!時間爲:"+new Date());
timer.cancel();//取消自身
}
}
static public class MyTaskB extends TimerTask{
@Override
public void run() {
System.out.println("B運行了!時間爲:"+new Date());
}
}
public static void main(String[] args) {
try {
MyTaskA task = new MyTaskA();
MyTaskB task2 = new MyTaskB();
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString1 = "2020-06-29 15:56:00";
Date dateRef2 = sdf1.parse(dateString1);
System.out.println("字符串2時間:"+dateRef2+"當前時間:"+new Date());
timer.schedule(task,dateRef2,4000);
timer.schedule(task2,dateRef2,4000);
}catch (ParseException e){
e.printStackTrace();
}
}
}
最後的運行結果如下,全部任務被清除,且進程被銷燬,按鈕變爲灰色
字符串2時間:Mon Jun 29 15:56:00 CST 2020當前時間:Mon Jun 29 15:59:33 CST 2020
A運行了!時間爲:Mon Jun 29 15:59:33 CST 2020
Timer類中的cancel()使用注意事項:
- Timer類中的cancel()方法有時並不一定會停止執行計劃任務,而是正常執行,這是因爲Timer類中cancel()方法有時並沒有爭搶到Queue鎖,所以TimerTask類中的任務可以正常被執行。
5.方法schedule(TimerTask task,long delay)方法的測試
該方法的作用是以執行schedule(TimerTask task,long delay)方法當前的時間爲參考時間,在此時間基礎上延遲指定的毫秒數執行一次TimerTask任務。
6.方法schedule(TimerTask task,long delay,long period)方法的測試
該方法的作用是以執行schedule(TimerTask task,long delay)方法當前的時間爲參考時間,在此時間基礎上延遲指定的毫秒數執行一次TimerTask任務,再以某一間隔時間無限次數地執行某一任務。
7.方法scheduleAtFixedRate(TimerTask task,Date firstTime,long period)方法的測試
-
方法schedule和方法scheduleAtFixedRate都會按順序執行,所以不用考慮非線程安全的情況
-
方法schedule和方法scheduleAtFixedRate主要的區別只在於是否具有追趕性的情況
-
使用schedule方法:如果執行任務的時間沒有被延時,那麼下一次任務的執行時間參考的是上一次任務的“開始”時間來計算。
-
使用scheduleAtFixedRate方法:如果執行任務的時間沒有被延時,那麼下一次任務的執行時間參考的是上一次任務的“開始”時間來計算。
-
延時的情況則沒有區別,兩者都是下一次執行的時間參考的都是上一次任務“結束”時的時間來計算。
7.1 測試schedule不延時的情況
創建Run4.java類:
public class Run4 {
private static Timer timer = new Timer();
private static int runCount = 0;
static public class MyTask1 extends TimerTask{
@Override
public void run() {
try {
System.out.println("1運行了!時間爲:"+new Date());
Thread.sleep(1000);
System.out.println(" 1 end 了!時間爲:"+new Date());
runCount++;
if(runCount == 5){
timer.cancel();
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
try {
MyTask1 task = new MyTask1();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2020-06-29 15:50:00";
Date dateRef = sdf.parse(dateString);
System.out.println("字符串時間:"+dateRef+"當前時間:"+new Date());
timer.schedule(task,dateRef,3000);
}catch (ParseException e){
e.printStackTrace();
}
}
}
最後的運行結果如下:在不延時的情況下,如果執行任務的時間沒有被延時,則下一次執行任務的時間是上一次任務的開始時間加上delay的時間
字符串時間:Mon Jun 29 15:50:00 CST 2020當前時間:Mon Jun 29 16:14:23 CST 2020
1運行了!時間爲:Mon Jun 29 16:14:23 CST 2020
1 end 了!時間爲:Mon Jun 29 16:14:24 CST 2020
1運行了!時間爲:Mon Jun 29 16:14:26 CST 2020
1 end 了!時間爲:Mon Jun 29 16:14:27 CST 2020
1運行了!時間爲:Mon Jun 29 16:14:29 CST 2020
1 end 了!時間爲:Mon Jun 29 16:14:30 CST 2020
1運行了!時間爲:Mon Jun 29 16:14:32 CST 2020
1 end 了!時間爲:Mon Jun 29 16:14:33 CST 2020
1運行了!時間爲:Mon Jun 29 16:14:35 CST 2020
1 end 了!時間爲:Mon Jun 29 16:14:36 CST 2020
7.2 測試schedule延時的情況
public class Run2 {
private static Timer timer = new Timer();
private static int runCount = 0;
static public class MyTask1 extends TimerTask{
@Override
public void run() {
try {
System.out.println("1運行了!時間爲:"+new Date());
Thread.sleep(5000);
System.out.println("1結束了!時間爲:"+new Date());
runCount++;
if(runCount == 2){
timer.cancel();
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
try {
MyTask1 task = new MyTask1();
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString1 = "2020-06-29 15:31:00";
Date dateRef1 = sdf1.parse(dateString1);
System.out.println("字符串1時間:"+dateRef1+"當前時間:"+new Date());
timer.schedule(task,dateRef1,2000);
}catch (ParseException e){
e.printStackTrace();
}
}
}
最後的運行結果如下:如果任務被延時,那麼下一次任務的執行時間以上一次任務“結束”時的時間來參考計算。
字符串1時間:Mon Jun 29 15:31:00 CST 2020當前時間:Mon Jun 29 16:26:28 CST 2020
1運行了!時間爲:Mon Jun 29 16:26:28 CST 2020
1結束了!時間爲:Mon Jun 29 16:26:33 CST 2020
1運行了!時間爲:Mon Jun 29 16:26:33 CST 2020
1結束了!時間爲:Mon Jun 29 16:26:38 CST 2020
7.3 測試scheduleAtFixedRate方法任務不延時的情況
修改Run4.java類如下:
public class Run4 {
private static Timer timer = new Timer();
private static int runCount = 0;
static public class MyTask1 extends TimerTask{
@Override
public void run() {
try {
System.out.println("1運行了!時間爲:"+new Date());
Thread.sleep(1000);
System.out.println(" 1 end 了!時間爲:"+new Date());
runCount++;
if(runCount == 2){
timer.cancel();
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
try {
MyTask1 task = new MyTask1();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2020-06-29 15:50:00";
Date dateRef = sdf.parse(dateString);
System.out.println("字符串時間:"+dateRef+"當前時間:"+new Date());
timer.scheduleAtFixedRate(task,dateRef,3000);
}catch (ParseException e){
e.printStackTrace();
}
}
}
最後的運行結果爲下:在不延時的情況下,如果執行任務的時間沒有被延時,則下一次執行任務的時間是上一次任務的開始時間加上delay的時間
字符串時間:Mon Jun 29 15:50:00 CST 2020當前時間:Mon Jun 29 16:28:39 CST 2020
1運行了!時間爲:Mon Jun 29 16:28:39 CST 2020
1 end 了!時間爲:Mon Jun 29 16:28:40 CST 2020
1運行了!時間爲:Mon Jun 29 16:28:40 CST 2020
1 end 了!時間爲:Mon Jun 29 16:28:41 CST 2020
7.4 測試scheduleAtFixedRate方法任務延時的情況
修改Run2.java類如下:
public class Run2 {
private static Timer timer = new Timer();
private static int runCount = 0;
static public class MyTask1 extends TimerTask{
@Override
public void run() {
try {
System.out.println("1運行了!時間爲:"+new Date());
Thread.sleep(5000);
System.out.println("1結束了!時間爲:"+new Date());
runCount++;
if(runCount == 2){
timer.cancel();
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
try {
MyTask1 task = new MyTask1();
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString1 = "2020-06-29 15:31:00";
Date dateRef1 = sdf1.parse(dateString1);
System.out.println("字符串1時間:"+dateRef1+"當前時間:"+new Date());
timer.scheduleAtFixedRate(task,dateRef1,2000);
}catch (ParseException e){
e.printStackTrace();
}
}
}
最後的運行結果如下:如果任務被延時,那麼下一次任務的執行時間以上一次任務“開始”時的時間來參考計算。
字符串1時間:Mon Jun 29 15:31:00 CST 2020當前時間:Mon Jun 29 16:31:00 CST 2020
1運行了!時間爲:Mon Jun 29 16:31:00 CST 2020
1結束了!時間爲:Mon Jun 29 16:31:05 CST 2020
1運行了!時間爲:Mon Jun 29 16:31:05 CST 2020
1結束了!時間爲:Mon Jun 29 16:31:10 CST 2020
8.驗證schedule不具有追趕性
創建Run6.java類如下:
public class Run6 {
public static Timer timer = new Timer();
static public class MyTask extends TimerTask{
@Override
public void run() {
System.out.println("運行了!時間爲:"+new Date());
System.out.println("結束運行了!時間爲:"+new Date());
}
}
public static void main(String[] args) throws InterruptedException {
MyTask myTask = new MyTask();
System.out.println("現在執行時間爲:" + new Date());
Calendar c = Calendar.getInstance();
c.set(Calendar.SECOND, c.get(Calendar.SECOND) +20);
Date runDate = c.getTime();
System.out.println("計劃執行時間爲:" + runDate);
Timer timer = new Timer();
//調用的是schedule方法,驗證其不具追趕性
timer.schedule(myTask, runDate, 4000);
}
}
運行結果如下:Mon Jun 29 16:43:36 CST 2020到Mon Jun 29 16:43:56 CST 2020之間的時間被取消掉,不執行了,這就是Task任務不追趕。
現在執行時間爲:Mon Jun 29 16:43:36 CST 2020
計劃執行時間爲:Mon Jun 29 16:43:56 CST 2020
運行了!時間爲:Mon Jun 29 16:43:56 CST 2020
結束運行了!時間爲:Mon Jun 29 16:43:56 CST 2020
運行了!時間爲:Mon Jun 29 16:44:00 CST 2020
結束運行了!時間爲:Mon Jun 29 16:44:00 CST 2020
9.驗證scheduleAtFixedRate具有追趕性
修改Run6.java如下:
public class Run6 {
public static Timer timer = new Timer();
static public class MyTask extends TimerTask{
@Override
public void run() {
System.out.println("運行了!時間爲:"+new Date());
System.out.println("結束運行了!時間爲:"+new Date());
}
}
public static void main(String[] args) throws InterruptedException {
MyTask myTask = new MyTask();
System.out.println("現在執行時間爲:" + new Date());
Calendar c = Calendar.getInstance();
c.set(Calendar.SECOND, c.get(Calendar.SECOND) -20);
Date runDate = c.getTime();
System.out.println("計劃執行時間爲:" + runDate);
Timer timer = new Timer();
//調用的是schedule方法,驗證其不具追趕性
timer.scheduleAtFixedRate(myTask, runDate, 4000);
}
}
最後運行結果如下:16:51:38-16:51:58的時間被補充執行:本來我的時間間隔爲4s執行一次,但是前期沒有按照我的時間間隔來執行。
追趕性就是如果我們設置任務開始的時間在當前時間之前,那麼他會計算出兩個時間的差,計算出時間差內能夠執行任務多少次,然後立即執行,執行完這個次數之後再按照我定的時間間隔做
現在執行時間爲:Mon Jun 29 17:05:07 CST 2020
計劃執行時間爲:Mon Jun 29 17:04:47 CST 2020
運行了!時間爲:Mon Jun 29 17:05:07 CST 2020
結束運行了!時間爲:Mon Jun 29 17:05:07 CST 2020
運行了!時間爲:Mon Jun 29 17:05:07 CST 2020
結束運行了!時間爲:Mon Jun 29 17:05:07 CST 2020
運行了!時間爲:Mon Jun 29 17:05:07 CST 2020
當執行任務的時間大於週期間隔時,會發生什麼呢?
(1)schedule方法:下一次執行時間相對於 上一次 實際執行完成的時間點 ,因此執行時間會不斷延後
(2)scheduleAtFixedRate方法:下一次執行時間相對於上一次開始的 時間點 ,因此執行時間不會延後,存在併發性
使用schedule:下一次的執行時間點=上一次程序執行完成的時間點+間隔時間
使用scheduleAtFixedRate:下一次的執行時間點=上一次程序開始執行的時間點+間隔時間 ;並且如果間隔時間爲5秒,因爲前一個任務要執行>5秒,而當前任務已經開始執行了,因此兩個任務間存在重疊,需要考慮線程同步