到這里我們做一個完整的例子來說明線程產(chǎn)生的方式不同而生成的線程的區(qū)別:
package debug;
import java.io.*;
import java.lang.Thread;
class MyThread extends Thread{
public int x = 0;
public void run(){
System.out.println(++x);
}
}
class R implements Runnable{
private int x = 0;
public void run(){
System.out.println(++x);
}
}
public class Test {
public static void main(String[] args) throws Exception{
for(int i=0;i<10;i++){
Thread t = new MyThread();
t.start();
}
Thread.sleep(10000);//讓上面的線程運行完成
R r = new R();
for(int i=0;i<10;i++){
Thread t = new Thread(r);
t.start();
}
}
} 上面10個線程對象產(chǎn)生的10個線程運行時打印了10次1。下面10個線程對象產(chǎn)生的10個線程運行時打印了1到10。我們把下面的10個線程稱為同一實例(Runnable實例)的多個線程 。
下節(jié)我們將研究線程對象方法,還是那句話,一般文檔中可以讀到的內(nèi)容我不會介紹太多
請大家自己了解。
線程對象的幾個重要的方法
盡管線程對象的常用方法可以通過API文檔來了解,但是有很多方法僅僅從API說明是無法詳細了解的。
本來打算用一節(jié)的篇幅來把線程方法中一些重要的知識說完,但這樣下來估計要很常的篇幅,可能要用好幾節(jié)才能說把和線程方法相關(guān)的一些重要的知識說完。
首先我們接基礎(chǔ)篇(二)來說明start()方法。
一個線程對象生成后,如果要產(chǎn)生一個執(zhí)行的線程,就一定要調(diào)用它的start()方法.在介紹這個方法時不得不同時說明run方法.其實線程對象的run方法完全是一個接口回調(diào)方法,它是你這個線程對象要完成的具體邏輯.簡單說你要做什么就你在run中完成,而如何做,什么時候做就不需要你控制了,你只要調(diào)用start()方法,JVM就會管理這個線程對象讓它產(chǎn)生一個線程并注冊到線程處理系統(tǒng)中。
從表面上看,start()方法調(diào)用了run()方法,事實上,start()方法并沒有直接調(diào)用run方法.在JDK1.5以前 start()方法是本地方法,它如何最終調(diào)用run方法已經(jīng)不是JAVA程序員所能了解的.而在JDK1.5中,原來的那個本地start()方法被 start0()代替,另個一個純JAVA的start()中調(diào)用本地方法start0(),而在start()方法中做了一個驗證,就是對一個全局變量 (對象變量)started做檢驗,如果為true,則start()拋出異常,不會調(diào)用本地方法start0(),否則,先將該變量設(shè)有true,然后調(diào)用start0()。
從中我們可以看到這個為了控制一個線程對象只能運行成功一次start()方法.這是因為線程的運行要獲取當前環(huán)境,包括安全,父線程的權(quán)限, 優(yōu)先級等條件,如果一個線程對象可以運行多次,那么定義一個static 的線程在一個環(huán)境中獲取相應(yīng)權(quán)限和優(yōu)先級,運行完成后它在另一個環(huán)境中利用原來的權(quán)限和優(yōu)先級等屬性在當前環(huán)境中運行,這樣就造成無法預(yù)知的結(jié)果.簡單說來,讓一個線程對象只能成功運行一次,是基于對線程管理的需要。
start()方法最本質(zhì)的功能是從CPU中申請另一個線程空間來執(zhí)行 run()方法中的代碼,它和當前的線程是兩條線,在相對獨立的線程空間運行,也就是說,如果你直接調(diào)用線程對象的run()方法,當然也會執(zhí)行,但那是在當前線程中執(zhí)行,run()方法執(zhí)行完成后繼續(xù)執(zhí)行下面的代碼.而調(diào)用start()方法后,run()方法的代碼會和當前線程并發(fā)(單CPU)或并行 (多CPU)執(zhí)行。
所以請記住一句話[調(diào)用線程對象的run方法不會產(chǎn)生一個新的線程],雖然可以達到相同的執(zhí)行結(jié)果,但執(zhí)行過程和執(zhí)行效率不同。
[線程的interrupt()方法,interrupted()和isInterrupted()]
這三個方法是關(guān)系非常密切而且又比較復(fù)雜的,雖然它們各自的功能很清楚,但它們之間的關(guān)系有大多數(shù)人不是真正的了解。
先說interrupt()方法,它是實例方法,而它也是最奇怪的方法,在java語言中,線程最初被設(shè)計為"隱晦難懂"的東西,直到現(xiàn)在它的語義不沒有象它的名字那樣準確。大多數(shù)人以為,一個線程象調(diào)用了interrupt()方法,那它對應(yīng)的線程就應(yīng)該被中斷而拋出異常,事實中,當一個線程對象調(diào)用interrupt()方法,它對應(yīng)的線程并沒有被中斷,只是改變了它的中斷狀態(tài)。
使當前線程的狀態(tài)變以中斷狀態(tài),如果沒有其它影響,線程還會自己繼續(xù)執(zhí)行。
只有當線程執(zhí)行到sleep,wait,join等方法時,或者自己檢查中斷狀態(tài)而拋出異常的情況下,線程才會拋出異常。