interrupt():
中斷線程。interrupted():
測試當前線程是否已經中斷。isInterrupted():
測試線程是否已經中斷。join():
等待該線程終止。join(long millis):
等待該線程終止的時間最長爲 millis
毫秒。join(long millis, int nanos):
等待該線程終止的時間最長爲 millis
毫秒
+ nanos
納秒。run():
如果該線程是使用獨立的 Runnable
運行對象構造的,則調用該 Runnable
對象的 run
方法;否則,該方法不執行任何操作並返回。sleep(long millis, int nanos):
在指定的毫秒數加指定的納秒數內讓當前正在執行的線程休眠(暫停執行),此操作受到系統計時器和調度程序精度和準確性的影響。start():
使該線程開始執行;Java 虛擬機調用該線程的 run
方法。yield():
暫停當前正在執行的線程對象,並執行其他線程。isAlive():
測試線程是否處於活動狀態。- <SPAN style="FONT-FAMILY: SimSun">public class LifeCycle extends Thread
- {
- public void run()
- {
- int n = 0;
- while ((++n) < 1000);
- }
- public static void main(String[] args) throws Exception
- {
- LifeCycle thread1 = new LifeCycle();
- System.out.println("isAlive: " + thread1.isAlive());
- thread1.start();
- System.out.println("isAlive: " + thread1.isAlive());
- thread1.join(); // 等線程thread1結束後再繼續執行
- System.out.println("thread1已經結束!");
- System.out.println("isAlive: " + thread1.isAlive());
- }
- } </SPAN>
public class LifeCycle extends Thread
{
public void run()
{
int n = 0;
while ((++n) < 1000);
}
public static void main(String[] args) throws Exception
{
LifeCycle thread1 = new LifeCycle();
System.out.println("isAlive: " + thread1.isAlive());
thread1.start();
System.out.println("isAlive: " + thread1.isAlive());
thread1.join(); // 等線程thread1結束後再繼續執行
System.out.println("thread1已經結束!");
System.out.println("isAlive: " + thread1.isAlive());
}
}
要注意一下,在上面的代碼中使用了join方法,這個方法的主要功能是保證線程的run方法完成後程序才繼續運行,上面代碼的運行結果:
isAlive: false
isAlive: true
thread1已經結束!
isAlive: false
一但線程開始執行run方法,就會一直到這個run方法執行完成這個線程才退出。但在線程執行的過程中,可以通過兩個方法使線程暫時停止執行。這兩個方法是yield和sleep。thread.yield()在多線程程序中,爲了防止某線程獨佔CPU資源(這樣其它的線程就得不到"響應"了).可以讓當前執行的線程"休息"一下.但是這種thread.yield() 調用,並不保證下一個運行的線程就一定不是該線程.而使用sleep使線程休眠後,只能在設定的時間後使線程處於就緒狀態(在線程休眠結束後,線程不一定會馬上執行,只是進入了就緒狀態,等待着系統進行調度)。在使用sleep時要注意,不能在一個線程中來休眠另一個線程。如main方法中使用thread.sleep(2000)方法是無法使thread線程休眠2秒的,而只能使主線程休眠2秒。在使用sleep方法時有四點需要注意:
1. sleep方法有兩個重載形式,其中一個重載形式不僅可以設毫秒,而且還可以設納秒(1,000,000納秒等於1毫秒)。但大多數操作系統平臺上的Java虛擬機都無法精確到納秒,因此,如果對sleep設置了納秒,Java虛擬機將取最接近這個值的毫秒。
2. 在使用sleep方法時必須使用throws或try{...}catch{...}。因爲run方法無法使用throws,所以只能使用try{...}catch{...}。當在線程休眠的過程中,使用interrupt方法(這個方法將在2.3.3中討論)中斷線程時sleep會拋出一個InterruptedException異常。
3. sleep()使當前線程進入停滯狀態,所以執行sleep()的線程在指定的時間內肯定不會執行;yield()只是使當前線程重新回到可執行狀態,所以執行yield()的線程有可能在進入到可執行狀態後馬上又被執行。
4. sleep()可使優先級低的線程得到執行的機會,當然也可以讓同優先級和高優先級的線程有執行的機會;yield()只能使同優先級的線程有執行的機會。
- <SPAN style="FONT-FAMILY: SimSun">class TestThreadMethod extends Thread{
- public static int shareVar = 0;
- public TestThreadMethod(String name){
- super(name);
- }
- public void run(){
- for(int i=0; i<4; i++){
- System.out.print(Thread.currentThread().getName());
- System.out.println(" : " + i);
- //Thread.yield(); (1)
- /* (2) */
- try{
- Thread.sleep(3000);
- }
- catch(InterruptedException e){
- System.out.println("Interrupted");
- }}}
- }
- public class TestThread{
- public static void main(String[] args){
- TestThreadMethod t1 = new TestThreadMethod("t1");
- TestThreadMethod t2 = new TestThreadMethod("t2");
- t1.setPriority(Thread.MAX_PRIORITY);
- t2.setPriority(Thread.MIN_PRIORITY);
- t1.start();
- t2.start();
- }
- } </SPAN>
class TestThreadMethod extends Thread{
public static int shareVar = 0;
public TestThreadMethod(String name){
super(name);
}
public void run(){
for(int i=0; i<4; i++){
System.out.print(Thread.currentThread().getName());
System.out.println(" : " + i);
//Thread.yield(); (1)
/* (2) */
try{
Thread.sleep(3000);
}
catch(InterruptedException e){
System.out.println("Interrupted");
}}}
}
public class TestThread{
public static void main(String[] args){
TestThreadMethod t1 = new TestThreadMethod("t1");
TestThreadMethod t2 = new TestThreadMethod("t2");
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
}
}
運行結果爲:
- t1 : 0
- t1 : 1
- t2 : 0
- t1 : 2
- t2 : 1
- t1 : 3
- t2 : 2
- t2 : 3
由結果可見,通過sleep()可使優先級較低的線程有執行的機會。註釋掉代碼(2),並去掉代碼(1)的註釋,結果爲:
- t1 : 0
- t1 : 1
- t1 : 2
- t1 : 3
- t2 : 0
- t2 : 1
- t2 : 2
- t2 : 3
可見,調用yield(),不同優先級的線程永遠不會得到執行機會。
有二種方法可以使終止線程。
1. 使用退出標誌,使線程正常退出,也就是當run方法完成後線程終止。
2. 使用interrupt方法中斷線程。
1. 使用退出標誌終止線程
當run方法執行完後,線程就會退出。但有時run方法是永遠不會結束的。如在服務端程序中使用線程進行監聽客戶端請求,或是其他的需要循環處理的任務。在這種情況下,一般是將這些任務放在一個循環中,如while循環。如果想讓循環永遠運行下去,可以使用while(true){...}來處理。但要想使while循環在某一特定條件下退出,最直接的方法就是設一個boolean類型的標誌,並通過設置這個標誌爲true或false來控制while循環是否退出。下面給出了一個利用退出標誌終止線程的例子。
- <SPAN style="FONT-FAMILY: SimSun">public class ThreadFlag extends Thread
- {
- public volatile boolean exit = false;
- public void run()
- {
- while (!exit);
- }
- public static void main(String[] args) throws Exception
- {
- ThreadFlag thread = new ThreadFlag();
- thread.start();
- sleep(5000); // 主線程延遲5秒
- thread.exit = true; // 終止線程thread
- thread.join();
- System.out.println("線程退出!");
- }
- } </SPAN>
public class ThreadFlag extends Thread
{
public volatile boolean exit = false;
public void run()
{
while (!exit);
}
public static void main(String[] args) throws Exception
{
ThreadFlag thread = new ThreadFlag();
thread.start();
sleep(5000); // 主線程延遲5秒
thread.exit = true; // 終止線程thread
thread.join();
System.out.println("線程退出!");
}
}
在上面代碼中定義了一個退出標誌exit,當exit爲true時,while循環退出,exit的默認值爲false。在定義exit時,使用了一個Java關鍵字volatile,這個關鍵字的目的是使exit同步,也就是說在同一時刻只能由一個線程來修改exit的值,
2. 使用interrupt方法終止線程
使用interrupt方法來終端線程可分爲兩種情況:
(1)線程處於阻塞狀態,如使用了sleep方法。
(2)使用while(!isInterrupted()){...}來判斷線程是否被中斷。
在第一種情況下使用interrupt方法,sleep方法將拋出一個InterruptedException例外,而在第二種情況下線程將直接退出。下面的代碼演示了在第一種情況下使用interrupt方法。
- <SPAN style="FONT-FAMILY: SimSun">public class ThreadInterrupt extends Thread
- {
- public void run()
- {
- try
- {
- sleep(50000); // 延遲50秒
- }
- catch (InterruptedException e)
- {
- System.out.println(e.getMessage());
- }
- }
- public static void main(String[] args) throws Exception
- {
- Thread thread = new ThreadInterrupt();
- thread.start();
- System.out.println("在50秒之內按任意鍵中斷線程!");
- System.in.read();
- thread.interrupt();
- thread.join();
- System.out.println("線程已經退出!");
- }
- } </SPAN>
public class ThreadInterrupt extends Thread
{
public void run()
{
try
{
sleep(50000); // 延遲50秒
}
catch (InterruptedException e)
{
System.out.println(e.getMessage());
}
}
public static void main(String[] args) throws Exception
{
Thread thread = new ThreadInterrupt();
thread.start();
System.out.println("在50秒之內按任意鍵中斷線程!");
System.in.read();
thread.interrupt();
thread.join();
System.out.println("線程已經退出!");
}
}
在50秒之內按任意鍵中斷線程!
sleep interrupted
線程已經退出!
在調用interrupt方法後, sleep方法拋出異常,然後輸出錯誤信息:sleep interrupted。注意:在Thread類中有兩個方法可以判斷線程是否通過interrupt方法被終止。一個是靜態的方法interrupted(),一個是非靜態的方法isInterrupted(),這兩個方法的區別是interrupted用來判斷當前線是否被中斷,而isInterrupted可以用來判斷其他線程是否被中斷。因此,while (!isInterrupted())也可以換成while (!Thread.interrupted())。
線程的狀態(State)
新生狀態(New): 當一個線程的實例被創建即使用new關鍵字和Thread類或其子類創建一個線程對象後,此時該線程處於新生(new)狀態,處於新生狀態的線程有自己的內存空間,但該線程並沒有運行,此時線程還不是活着的(not alive);
就緒狀態(Runnable): 通過調用線程實例的start()方法來啓動線程使線程進入就緒狀態(runnable);處於就緒狀態的線程已經具備了運行條件,但還沒有被分配到CPU即不一定會被立即執行,此時處於線程就緒隊列,等待系統爲其分配CPCU,等待狀態並不是執行狀態; 此時線程是活着的(alive);
運行狀態(Running): 一旦獲取CPU(被JVM選中),線程就進入運行(running)狀態,線程的run()方法纔開始被執行;在運行狀態的線程執行自己的run()方法中的操作,直到調用其他的方法而終止、或者等待某種資源而阻塞、或者完成任務而死亡;如果在給定的時間片內沒有執行結束,就會被系統給換下來回到線程的等待狀態;此時線程是活着的(alive);
阻塞狀態(Blocked):通過調用join()、sleep()、wait()或者資源被暫用使線程處於阻塞(blocked)狀態;處於Blocking狀態的線程仍然是活着的(alive)
死亡狀態(Dead):當一個線程的run()方法運行完畢或被中斷或被異常退出,該線程到達死亡(dead)狀態。此時可能仍然存在一個該Thread的實例對象,當該Thready已經不可能在被作爲一個可被獨立執行的線程對待了,線程的獨立的call stack已經被dissolved。一旦某一線程進入Dead狀態,他就再也不能進入一個獨立線程的生命週期了。對於一個處於Dead狀態的線程調用start()方法,會出現一個運行期(runtime exception)的異常;處於Dead狀態的線程不是活着的(not alive)。
線程狀態圖
每個類都有自己的優先級,一般property用1-10的整數表示,默認優先級是5,優先級最高是10;優先級高的線程並不一定比優先級低的線程執行的機會高,只是執行的機率高;默認一個線程的優先級和創建他的線程優先級相同;
2)Thread.sleep()/sleep(long millis)
當前線程睡眠/millis的時間(millis指定睡眠時間是其最小的不執行時間,因爲sleep(millis)休眠到達後,無法保證會被JVM立即調度);sleep()是一個靜態方法(static method) ,所以他不會停止其他的線程也處於休眠狀態;線程sleep()時不會失去擁有的對象鎖。 作用:保持對象鎖,讓出CPU,調用目的是不讓當前線程獨自霸佔該進程所獲取的CPU資源,以留一定的時間給其他線程執行的機會;
讓出CPU的使用權,給其他線程執行機會、讓同等優先權的線程運行(但並不保證當前線程會被JVM再次調度、使該線程重新進入Running狀態),如果沒有同等優先權的線程,那麼yield()方法將不會起作用。
當一個線程執行到wait()方法時,他就進入到一個和該對象相關的等待池(Waiting Pool)中,同時失去了對象的機鎖—暫時的,wait後還要返還對象鎖。當前線程必須擁有當前對象的鎖,如果當前線程不是此鎖的擁有者,會拋出IllegalMonitorStateException異常,所以wait()必須在synchronized block中調用。
喚醒在當前對象等待池中等待的第一個線程/所有線程。notify()/notifyAll()也必須擁有相同對象鎖,否則也會拋出IllegalMonitorStateException異常。