為創建一個線程,最簡單的方法就是從Thread類繼承。這個類包含了創建和運行線程所需的一切東西。Thread最重要的方法是run()。但為了使用run(),必須對其進行過載或者覆蓋,使其能充分按自己的吩咐行事。因此,run()屬於那些會與程序中的其他線程“並發”或“同時”執行的代碼。
下面這個例子可創建任意數量的線程,並通過為每個線程分配一個獨一無二的編號(由一個靜態變量產生),從而對不同的線程進行跟蹤。Thread的run()方法在這裡得到了覆蓋,每通過一次循環,計數就減1——計數為0時則完成循環(此時一旦返回run(),線程就中止運行)。
//: SimpleThread.java // Very simple Threading example public class SimpleThread extends Thread { private int countDown = 5; private int threadNumber; private static int threadCount = 0; public SimpleThread() { threadNumber = ++threadCount; System.out.println("Making " + threadNumber); } public void run() { while(true) { System.out.println("Thread " + threadNumber + "(" + countDown + ")"); if(--countDown == 0) return; } } public static void main(String[] args) { for(int i = 0; i < 5; i++) new SimpleThread().start(); System.out.println("All Threads Started"); } } ///:~
run()方法幾乎肯定含有某種形式的循環——它們會一直持續到線程不再需要為止。因此,我們必須規定特定的條件,以便中斷並退出這個循環(或者在上述的例子中,簡單地從run()返回即可)。run()通常采用一種無限循環的形式。也就是說,通過阻止外部發出對線程的stop()或者destroy()調用,它會永遠運行下去(直到程序完成)。
在main()中,可看到創建並運行了大量線程。Thread包含了一個特殊的方法,叫作start(),它的作用是對線程進行特殊的初始化,然後調用run()。所以整個步驟包括:調用構建器來構建對象,然後用start()配置線程,再調用run()。如果不調用start()——如果適當的話,可在構建器那樣做——線程便永遠不會啟動。
下面是該程序某一次運行的輸出(注意每次運行都會不同):
Making 1 Making 2 Making 3 Making 4 Making 5 Thread 1(5) Thread 1(4) Thread 1(3) Thread 1(2) Thread 2(5) Thread 2(4) Thread 2(3) Thread 2(2) Thread 2(1) Thread 1(1) All Threads Started Thread 3(5) Thread 4(5) Thread 4(4) Thread 4(3) Thread 4(2) Thread 4(1) Thread 5(5) Thread 5(4) Thread 5(3) Thread 5(2) Thread 5(1) Thread 3(4) Thread 3(3) Thread 3(2) Thread 3(1)
可注意到這個例子中到處都調用了sleep(),然而輸出結果指出每個線程都獲得了屬於自己的那一部分CPU執行時間。從中可以看出,盡管sleep()依賴一個線程的存在來執行,但卻與允許或禁止線程無關。它只不過是另一個不同的方法而已。
亦可看出線程並不是按它們創建時的順序運行的。事實上,CPU處理一個現有線程集的順序是不確定的——除非我們親自介入,並用Thread的setPriority()方法調整它們的優先級。
main()創建Thread對象時,它並未捕獲任何一個對象的句柄。普通對象對於垃圾收集來說是一種“公平競賽”,但線程卻並非如此。每個線程都會“注冊”自己,所以某處實際存在著對它的一個引用。這樣一來,垃圾收集器便只好對它“瞠目以對”了。