在JDK類庫中Timer類主要負責計划任務的功能,也就是在指定的時間開始執行某一個任務。此類也常用來做一下周期性同步工作,代替它的有quartz、SpringTask。Timer類的主要作用是設置計划任務,但封裝任務的類是TimerTask類(實際該類是一個抽象類,執行任務的代碼要放在該類的子類中)。Timer類的主要方法列表如下:
構造方法

成員方法:

Timer內部維護一個 TimerThread 線程,而且線程名字與Thread類一樣,默認用一個靜態成員變量進行生成全局序列。
public class Timer { private final TaskQueue queue = new TaskQueue(); /** * The timer thread. */ private final TimerThread thread = new TimerThread(queue); /** * This ID is used to generate thread names. */ private final static AtomicInteger nextSerialNumber = new AtomicInteger(0);
TimerThread的run方法中while循環獲取TaskQueue中的task並且執行任務(mainLoop中執行執行task.run()方法--相當於同步調用)
class TimerThread extends Thread { boolean newTasksMayBeScheduled = true; private TaskQueue queue; TimerThread(TaskQueue queue) { this.queue = queue; } public void run() { try { mainLoop(); } finally { synchronized(queue) { newTasksMayBeScheduled = false; queue.clear(); // Eliminate obsolete references } } } /** * The main timer loop. (See class comment.) */ private void mainLoop() { while (true) { 。。。。。。 } }
TaskQueue用於存放TimerTask任務(內部維護一個TimerTask數組)。
class TaskQueue { private TimerTask[] queue = new TimerTask[128];
。。。
TimerTask是一個繼承Runnable接口的抽象類:
public abstract class TimerTask implements Runnable { final Object lock = new Object();
。。。
TimerTask有一個cancel方法用於將當前任務從任務隊列刪除。(也就是隊列中不會執行該任務,如果正在執行中調用該方法會執行完畢)
public boolean cancel() { synchronized(lock) { boolean result = (state == SCHEDULED); state = CANCELLED; return result; } }
1. Sschedule(task, Date)的用法
此方法用於在指定的時間執行一次task。默認的執行完畢不會結束線程,因為timer的成員屬性thread默認是非守護線程,而且其run方法中通過輪詢同步調用task的run()方法。
例子1:一個簡單的測試:(在未來時間執行一次任務---會到指定之間執行run()方法)
package cn.qlq.thread.fourteen; import java.util.Date; import java.util.Timer; import java.util.TimerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Demo1 extends TimerTask { private static final Logger LOGGER = LoggerFactory.getLogger(Demo1.class); public static void main(String[] args) throws InterruptedException { LOGGER.info("start "); Timer timer = new Timer(); Demo1 task = new Demo1(); Date runtime = new Date(System.currentTimeMillis() + 5000); timer.schedule(task, runtime); LOGGER.info("end "); } @Override public void run() { for (int i = 0; i < 5; i++) { LOGGER.info("threadName -> {},value -> {}", Thread.currentThread().getName(), i + ""); try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
結果:(可以看到先打印的end,后執行的run中方法,並且執行完畢之后進程並未銷毀。也驗證了是新開一個線程進行操作,並且線程是非守護線程)

查看源碼:
public Timer() { this("Timer-" + serialNumber()); } public Timer(String name) { thread.setName(name); thread.start(); }
如果將上面Timer中的TimerThread設為守護線程將不會執行run中方法,因為main線程執行完畢,沒有非守護線程的存在,所以守護線程也銷毀導致進程銷毀。
另一種辦法是等待執行完之后調用timer.cancel()
public static void main(String[] args) throws InterruptedException { LOGGER.info("start "); Timer timer = new Timer(); Demo1 task = new Demo1(); Date runtime = new Date(System.currentTimeMillis() + 5000); timer.schedule(task, runtime); LOGGER.info("end "); Thread.sleep(6*1000); timer.cancel(); }
結果:

例子2:多個任務在之前的時間執行---(線性順序執行多個task,是從queue中獲取task然后執行,如果時間早於當前時間會馬上執行任務)
package cn.qlq.thread.fourteen; import java.util.Date; import java.util.Timer; import java.util.TimerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Demo1 extends TimerTask { private static final Logger LOGGER = LoggerFactory.getLogger(Demo1.class); private String name; public static void main(String[] args) throws InterruptedException { LOGGER.info("start "); Timer timer = new Timer(); Demo1 task = new Demo1("t1"); Demo1 task2 = new Demo1("t2"); Date runtime = new Date(System.currentTimeMillis() - 5000); timer.schedule(task, runtime); timer.schedule(task2, runtime); LOGGER.info("end "); } @Override public void run() { for (int i = 0; i < 5; i++) { LOGGER.info("threadName -> {},value -> {},taskName->{}", Thread.currentThread().getName(), i + "", name); try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public String getName() { return name; } public void setName(String name) { this.name = name; } public Demo1(String name) { super(); this.name = name; } }
結果:

2. Sschedule(TimerTask task, Date firsttime,long period)的用法
此方法用於在指定的時間執行一次之后任務之后,在指定的period的時間間隔后不停的執行任務
package cn.qlq.thread.fourteen; import java.util.Date; import java.util.Timer; import java.util.TimerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Demo2 extends TimerTask { private static final Logger LOGGER = LoggerFactory.getLogger(Demo2.class); private String name; public static void main(String[] args) throws InterruptedException { LOGGER.info("start "); Timer timer = new Timer(); Demo2 task = new Demo2("t1"); Demo2 task2 = new Demo2("t2"); Date runtime = new Date(System.currentTimeMillis() + 5000); timer.schedule(task, runtime, 20 * 1000); timer.schedule(task2, runtime, 20 * 1000); LOGGER.info("end "); } @Override public void run() { for (int i = 0; i < 5; i++) { LOGGER.info("threadName -> {},value -> {},taskName->{}", Thread.currentThread().getName(), i + "", name); try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public String getName() { return name; } public void setName(String name) { this.name = name; } public Demo2(String name) { super(); this.name = name; } }
結果:(時間間隔是從任務開始執行計算的,也就是從當前任務執行的開始時間到下次任務開始時間的間隔是20秒)

3. Shedule(TimerTask task, long delay)的用法
以當前時間為參考,在延遲指定的秒數后執行一次性任務;如果延遲時間是負數會拋出IllegalArgumentException異常。
package cn.qlq.thread.fourteen; import java.util.Timer; import java.util.TimerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Demo3 extends TimerTask { private static final Logger LOGGER = LoggerFactory.getLogger(Demo3.class); private String name; public static void main(String[] args) throws InterruptedException { LOGGER.info("start "); Timer timer = new Timer(); Demo3 task = new Demo3("t1"); timer.schedule(task, 2 * 1000); LOGGER.info("end "); } @Override public void run() { for (int i = 0; i < 5; i++) { LOGGER.info("threadName -> {},value -> {},taskName->{}", Thread.currentThread().getName(), i + "", name);try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public String getName() { return name; } public void setName(String name) { this.name = name; } public Demo3(String name) { super(); this.name = name; } }
結果:(執行完線程不會關閉)

4. Shedule(TimerTask task, long delay,long period)的用法
以當前時間為參考,在延遲指定的秒數后第一次執行任務;如果延遲時間是負數會拋出IllegalArgumentException異常。並且在period后重復執行任務,執行時間是從上次任務結束時間開始計算。凡是帶period的都會在時間間隔后重復執行。
package cn.qlq.thread.fourteen; import java.util.Timer; import java.util.TimerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Demo4 extends TimerTask { private static final Logger LOGGER = LoggerFactory.getLogger(Demo4.class); private String name; public static void main(String[] args) throws InterruptedException { LOGGER.info("start "); Timer timer = new Timer(); Demo4 task = new Demo4("t1"); timer.schedule(task, 2 * 1000, 10 * 1000); LOGGER.info("end "); } @Override public void run() { for (int i = 0; i < 5; i++) { LOGGER.info("threadName -> {},value -> {},taskName->{}", Thread.currentThread().getName(), i + "", name); try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public String getName() { return name; } public void setName(String name) { this.name = name; } public Demo4(String name) { super(); this.name = name; } }
結果

5. sheduleAtFixedRate(TimerTask task, Date firstTime,long period)的用法
方法schedule和scheduleAtFixedRate都會按順序執行,所以不要考慮非線程安全的情況。
在有延時和沒有延時的情況下,周期性的任務的下次任務開始時間都是相對於上次任務的開始時間進行延遲(這個在並發編程書中說的是有延遲的情況下相對於結束時間,但是自己測的是相對於開始時間)
schedule和scheduleAtFixedRate的區別在於,如果指定開始執行的時間在當前系統運行時間之前,scheduleAtFixedRate會把已經過去的時間也作為周期執行,而schedule不會把過去的時間算上
1.scheduleAtFixedRate(TimerTask task,long delay,long period) 有延遲,下次任務相對於上次任務的開始時間開始執行:
package cn.qlq.thread.fourteen; import java.util.Timer; import java.util.TimerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Demo5 extends TimerTask { private static final Logger LOGGER = LoggerFactory.getLogger(Demo5.class); private String name; public static void main(String[] args) throws InterruptedException { LOGGER.info("start "); Timer timer = new Timer(); Demo5 task = new Demo5("t1"); timer.scheduleAtFixedRate(task, 2 * 1000, 10 * 1000); LOGGER.info("end "); } @Override public void run() { for (int i = 0; i < 5; i++) { LOGGER.info("threadName -> {},value -> {},taskName->{}", Thread.currentThread().getName(), i + "", name); try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public String getName() { return name; } public void setName(String name) { this.name = name; } public Demo5(String name) { super(); this.name = name; } }
結果:

2.scheduleAtFixedRate(TimerTask task,Date startTime,long period) 指定開始執行的時間在當前系統運行時間之前,會把已經過去的時間也作為周期執行
package cn.qlq.thread.fourteen; import java.util.Date; import java.util.Timer; import java.util.TimerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Demo6 extends TimerTask { private static final Logger LOGGER = LoggerFactory.getLogger(Demo6.class); private String name; public static void main(String[] args) throws InterruptedException { LOGGER.info("start "); Timer timer = new Timer(); Demo6 task = new Demo6("t1"); timer.scheduleAtFixedRate(task, new Date(System.currentTimeMillis() - 5000), 10 * 1000); LOGGER.info("end "); } @Override public void run() { for (int i = 0; i < 3; i++) { LOGGER.info("threadName -> {},value -> {},taskName->{}", Thread.currentThread().getName(), i + "", name); try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public String getName() { return name; } public void setName(String name) { this.name = name; } public Demo6(String name) { super(); this.name = name; } }
結果:(第二次任務把之前的5秒鍾也包含在延遲時間內)

補充:
1.TimerTask有一個cancel方法用於將當前任務從任務隊列刪除。(也就是隊列中不會執行該任務,如果正在執行中調用該方法會執行完畢)
package cn.qlq.thread.fourteen; import java.util.Date; import java.util.Timer; import java.util.TimerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Demo2 extends TimerTask { private static final Logger LOGGER = LoggerFactory.getLogger(Demo2.class); private String name; public static void main(String[] args) throws InterruptedException { LOGGER.info("start "); Timer timer = new Timer(); Demo2 task = new Demo2("t1"); Demo2 task2 = new Demo2("t2"); Date runtime = new Date(System.currentTimeMillis() + 5000); timer.schedule(task, runtime, 20 * 1000); timer.schedule(task2, runtime, 20 * 1000); LOGGER.info("end "); } @Override public void run() { for (int i = 0; i < 5; i++) { LOGGER.info("threadName -> {},value -> {},taskName->{}", Thread.currentThread().getName(), i + "", name); if (name.equals("t1")) { this.cancel(); } try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public String getName() { return name; } public void setName(String name) { this.name = name; } public Demo2(String name) { super(); this.name = name; } }
結果:(t1任務被刪除)

2.Timer的cancel方法用於清空所有任務隊列(如果有任務正在執行會等任務執行完清空;有時候不一定會停掉,因為cancel方法不一定能搶到queue對象的鎖)
package cn.qlq.thread.fourteen; import java.util.Date; import java.util.Timer; import java.util.TimerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Demo2 extends TimerTask { private static final Logger LOGGER = LoggerFactory.getLogger(Demo2.class); private String name; public static void main(String[] args) throws InterruptedException { LOGGER.info("start "); Timer timer = new Timer(); Demo2 task = new Demo2("t1"); Demo2 task2 = new Demo2("t2"); Date runtime = new Date(System.currentTimeMillis()); timer.schedule(task, runtime, 20 * 1000); timer.schedule(task2, runtime, 20 * 1000); LOGGER.info("end "); Thread.sleep(2 * 1000); timer.cancel(); LOGGER.info("timer cancel"); } @Override public void run() { for (int i = 0; i < 5; i++) { LOGGER.info("threadName -> {},value -> {},taskName->{}", Thread.currentThread().getName(), i + "", name); if (name.equals("t1")) { this.cancel(); } try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public String getName() { return name; } public void setName(String name) { this.name = name; } public Demo2(String name) { super(); this.name = name; } }
結果:

