timer.cancel(); //Terminate the timer thread
}
}
public static void main(String args[]){
new Reminder(5);
System.out.println("Task scheduled.");
}
}
實現和調度有計時器線程執行的任務的基本部分。
1、 實現一個定制的TimerTask子類。run方法包含執行任務的代碼。
2、 通過實例化Timer類創建一個線程。
3、 實例化一個計時器任務對象(new RenmindTask())。
4、 調度這個計時器任務的執行。本例使用schedule方法。
讓一個任務在特定時間執行:
import java.util.Timer;
import java.util.TimerTask;
import java.util.Calendar;
import java.util.Date;
public class Reminder{
Timer timer;
public Reminder(int seconds){
timer = new Timer();
timer.schedule(new ReminderTask(), seconds*1000);
}
public Reminder(){
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 11);
calendar.set(Calendar.MINUTE, 9);
calendar.set(Calendar.SECOND, 0);
Date time = calendar.getTime();
timer = new Timer();
timer.schedule(new RemindTask2(), time);
}
class ReminderTask extends TimerTask{
public void run(){
System.out.println("Time's up!");
timer.cancel(); //Terminate the timer thread
}
}
class RemindTask2 extends TimerTask{
public void run(){
System.out.println("Time's up! Ring on.");
timer.cancel(); //Terminate the timer thread
}
}
public static void main(String args[]){
new Reminder(5);
new Reminder();
System.out.println("Task scheduled.");
}
}
在默認情況下,只要程序的計時器線程在運行,程序就一直運行。有四種方式可以終止計時器線程。
1、 在計時器線程上調用cancel方法。
2、 使計時器成為“守護線程”(deamon),辦法是這樣創建計時器: new Timer(true) 。如果程序中僅剩下守護線程,那么程序退出。
3、 在計時器的被調度的所有任務都完成后,刪除所有對Timer對象的引用。最后計時器的線程將退出。
4、 調用System.exit方法,使整個程序退出。
重復執行任務:
import java.util.Timer;
import java.util.TimerTask;
import java.awt.Toolkit;
/**
* Schedule a task that executes once every second.
*/
public class AnnoyingBeep {
Toolkit toolkit;
Timer timer;
public AnnoyingBeep() {
toolkit = Toolkit.getDefaultToolkit();
timer = new Timer();
timer.schedule(new RemindTask(),
0, //initial delay
1*1000); //subsequent rate
}
class RemindTask extends TimerTask {
int numWarningBeeps = 3;
public void run(){
if (numWarningBeeps > 0) {
toolkit.beep();
System.out.println("Beep!");
numWarningBeeps--;
} else {
toolkit.beep();
System.out.println("Time's up!");
//timer.cancel(); //Not necessary because we call System.exit
System.exit(0); //Stops the AWT thread (and everything else)
}
}
}
public static void main(String args[]) {
System.out.println("About to schedule task.");
new AnnoyingBeep();
System.out.println("Task scheduled.");
}
}
AnnoyingBeep程序使用schedule方法的三參數版本,指定它的任務應該馬上開始執行,并且每1秒執行一次。下面是所有可用來調度任務反復執行的方法:
schedule(TimerTask task, long delay, long period)
schedule(TimerTask task, Date time, long period)
scheduleAtFixedRate(TimerTask task, long delay, long period)
scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
在調度任務反復執行時,如果執行的平滑性很重要,那么應該使用schedule方法之一,如果一次蜂鳴聲因為某種原因推遲了,那么后續的所有的蜂鳴聲將相應延遲;如果時間的同步更重要,那么應該使用scheduleAtFixedRate方法之一,如果想讓程序在第一次蜂鳴后正好3秒退出,,如果一次蜂鳴聲因為某種原因延遲了,那么兩次蜂鳴的時間可能較近,小于1秒。
8.3 定制線程的run方法
有兩種技術可以為線程提供run方法:
1、 對Thread類進行子類化并覆蓋run方法
2、 實現Runnable接口
對Thread類進行子類化并覆蓋run方法
Thread類本身是一個Runnable對象。
public class SimpleThread extends Thread{
public SimpleThread(String str){
super(str);
}
public void run(){
for(int i = 0; i < 10; i++){
System.out.println(i + " " + getName());
try{
sleep((int)(Math.random()*1000));
}catch(InterruptedException e){}
}
System.out.println("Done! " + getName());
}
}
第一個方法是構造器,調用超類構造器設置線程名稱。
public class TwoThreadDemo{
public static void main(String[] args){
new SimpleThread("Jamaica").start();
new SimpleThread("Fiji").start();
}
}
實現Runnable接口
import java.awt.Graphics;
import java.util.*;
import java.text.DateFormat;
import java.applet.Applet;
public class Clock extends Applet implements Runnable {
private Thread clockThread = null;
public void start(){
if(clockThread == null){
clockThread = new Thread(this, "Clock");
clockThread.start();
}
}
public void run(){
Thread myThread = Thread.currentThread();
while(clockThread == myThread){
repaint();
try{
Thread.sleep(1000);
}catch(InterruptedException e){
//the VM doesn't want us to sleep anymore;
//so get back to work
}
}
}
public void paint(Graphics g){
//get the time and convert it to a date
Calendar cal = Calendar.getInstance();
Date date = cal.getTime();
//format it and display it
DateFormat dateFormatter = DateFormat.getTimeInstance();
g.drawString(dateFormatter.format(date), 5, 10);
}
//overrides Applet's stop method, not Thread's
public void stop(){
clockThread = null;
}
}
<html>
<head>
<title>Clock</title>
</head>
<body>
I'm now listening to OuRuoLa
<APPLET CODE= "Clock.class" WIDTH=150 HEIGHT=35></APPLET>
</body>
</html>
這個Clock applet 顯示當前時間并且每1秒更新一次。問題是會間接的閃爍,或許和CPU的工作狀態有關。用appletviewer Clock.html查看。
Clock applet的run方法進行循環,知道瀏覽器要求他停止。在循環的每次迭代期間,時鐘重新繪制它的顯示。
如果你的淚必須子類化另一個類,那么應該使用Runnable接口。
8.4 線程的生存周期
1、創建線程
clockThread = new Thread(this, "Clock"); 執行后Clock applet處于”新線程”狀態。此時線程僅僅是一個空的Thread對象,這時只能啟動線程,調用除start方法外的任何方法都是無意義的,而且會導致IllegalThreadStateException。實際上,只要在線程上調用一個方法,而此線程的狀態不允許此方法調用,那么運行時系統都會拋出IllegalThreadStateException。
2、啟動線程
clockThread.start(); start方法分配線程所需的系統資源,調度線程運行,并調用線程的run方法。
start方法返回后,線程處于“正在運行”狀態。實際情況要更復雜。如果計算機只有一個處理器,這樣就不可能同時運行所有”正在運行“的線程。Java運行時環境必須實現一個調度方案,以便在所有”正在運行“的線程分享處理器。所以在任何給定時刻,”正在運行”的線程可能正在等待輪到它使用CPU。
public void run(){
Thread myThread = Thread.currentThread();
while(clockThread == myThread){
repaint();
try{
Thread.sleep(1000);
}catch(InterruptedException e){
//the VM doesn't want us to sleep anymore;
//so get back to work
}
}
}
Clock的run方法在條件clockThread == myThread成立時一直循環。它使線程和applet平緩地退出。在循環中,applet重新繪制本身,然后讓線程睡眠1000毫秒。
1、 使線程不可運行
當以下事件之一發生時,線程變成“不可運行”狀態:
它的sleep方法被調用;
線程調用wait方法等待某個條件得到滿足;
線程因I/O而阻塞。
對于每個進入“不可運行“狀態的入口,有一個特定的不同出口將線程返回到”可運行“狀態。
如果一個線程已經進入睡眠狀態,那么必須經過指定的睡眠時間(毫秒數);
如果一個線程正在等待某個條件,那么另一個對象必須通過調用notify或notifyAll告知正在等待的線程條件發生了改變;
如果一個線程因I/O而阻塞,那么I/O必須完成。
2、 停止線程
自然消亡;
while(clockThread == myThread)這個條件表示,當前執行的線程不等于clockThread時循環退出。當你離開頁面時,運行此applet的應用程序調用applet的stop方法。然后,這個方法設置clockThread為null,由此讓主循環終止run方法。如果重新訪問這個頁面,那么再次調用start方法,時鐘在一個新線程中再次啟動。即使停止和啟動applet的時間快于循環一次的迭代,clockThread線程也將不同于myThread,循環將終止。
3、 isAlive方法
返回false,線程處于”新線程“狀態,或者已經消亡。返回true,線程處于”可運行“或者”不可運行“。