Java的內建線程支援
關於Java好的方面是它有內建的寫多線程的支援。Java的設計者知道多線程編程的價值,所以聰明決定在Java的核心部分就決定直接支援線程。在第7章“並發存取對象和變數”就闡述了在Java語言中,synchronized關鍵字如何被用來鎖住對象和類來控制並發存取資料。Thread和ThreadGroup類就在java.lang包的核心API中。通過wait( ) notify( ) 方法的支援,Java中所有類的父類Object具有線程間通訊的能力。即使當前作業系統不支援線程概念,一個寫好的JVM也可以類比多線程環境。在Java中,線程的支援不是事後的,而是從一開始設計時就包括進來的。
第2章:一個簡單的兩個線程例子
概述
這章展示了建立並在一個小的Java應用中運行線程是如何簡單。第一個線程總是在由JVM建立的“main”線程,該線程開始運行程式。該主線程然後建立第二個線程。每個線程將在控制台列印各自資訊,以此來證明他們好像以同步方式運行。
建立一個線程的步驟如下:
- 繼承java.lang.Thread類
- 在繼承Thread的子類中重寫run( )方法;
- 建立這個新類的執行個體
- 調用該執行個體的start( )方法
繼承java.lang.thread類
在JVM中每個線程與java.lang.Thread類的一個執行個體相關聯。這些Thread對象作為與底層作業系統線程互動的介面。通過該類中的方法,可以對線程進行開始,停止,中斷,命名、設定優先權和查詢有關線程目前狀態等操作。
[b]注意:{/b]有兩種方式可建立一個允許線程運行於其中的類。一種方式是繼承Thread類;另一種是繼承任意類並且實現Runnable介面。為了便於說明,繼承Thread類是最簡單的方法,本書開始就是用該方法。在實際中,實現Runnable介面可能工作地更好些。
在這個例子中,建立新線程的第一步是繼承java.lang.Thread類:
Java代碼
- public class TwoThread extends Thread {
- // ...
- }
public class TwoThread extends Thread { // ...}
TwoThread子類是一個(IS-A) Thread,繼承了父類的protected 和public成員。TwoThread除了從父類中繼承的其他行為之外,它還可以對線程進行開始、停止、中斷、命名、設定優先權以及查詢線程目前狀態等操作。
重寫run()方法
繼承Thread類之後,下一步就是重寫run()方法,因為Thread類中方法什麼也沒做:
Public void run() { }
當一個新線程開始運行,進入該線程的入口就是run()方法。run()中的第一條語句就是新線程執行的第一條語句。線程執行的每條語句都包含在run()方法中或包含在被run()方法直接或間接調用的其他方法中。從run()被調用之前到run()返回,新線程被認為是活著的。run()返回之後,線程就死掉了。當一個線程死了之後不能重新開始。
在本章的例子中,run()方法被重寫為迭代10次並且每次列印New thread資訊:
Java代碼
- public void run() {
- for ( int i = 0; i < 10; i++ ) {
- System.out.println(“New thread”);
- }
- }
public void run() { for ( int i = 0; i < 10; i++ ) { System.out.println(“New thread”); }}
迴圈完成之後,線程從run()方法返回,然後安靜死掉。
建立一個新線程 Spawning a New Thread
新線程從已經啟動並執行線程來建立。首先,構造一個新Thread執行個體。在此例中,一個新TwoThread對象將正好,因為TwoThread IS-A Thread:
TwoThread tt = new TwoThread();
下一步是調用start()方法準備開始執行線程(start()從Thread繼承而來):
tt.start();
start()調用後立即返回,不需要等待其他線程開始執行。在start()中,父線程非同步地從JVM中給線程發送器發出訊號,告訴它只要它方便,其他線程可以開始執行了。在未來某個不可預期的時間裡,其他線程將被啟用,並調用Thread對象的run()方法(在該例中是指TwoThread中實現的重寫方法run())。與此同時,原來的線程繼續執行start()方法後面的語句。
兩個線程並發、獨立地運行。在一個多處理器的機器上,這兩個線程可能在同一時刻真正都在運行,各自運行在自己的處理器上。
一個更常見的情況是只有一個處理器,JVM和OS共同工作在短時間調度每個線程。當其他線程被凍結,等待處理器的下一次機會時,每個線程就得到一次啟動並執行機會。這種線上程之間的環境切換是非常快的,給人一種同時執行的假象。
提示:一個新建立的線程可能在start()被調用之後的任何時間開始執行(進入run()方法)。這意味著start()方法之後的任何語句被執行之前,原來線程都可能被換出(swapped out)。
假如原來的線程正在執行下面的代碼:
stmt1();
tt.start();
新線程有一個run()方法如下:
Java代碼
- public void run() {
- stmtA();
- stmtB();
- }
- stmt2();
public void run() { stmtA(); stmtB();}stmt2();
處理器中實際執行的語句順序可能是stmt1(), tt.start(), stmt2(), stmtA(), and stmtB()。也可能是:stmt1(), tt.start(), stmtA(), stmtB(), and stmt2().也許還可能是另外一種順序。
重要的是要記住:儘管每個線程將執行各自語句的順序是已知和簡單的,但是實際運行在處理器上的語句卻是不確定的。對於程式的正確性而言,沒有一種特殊的順序可以依賴。
把所有放在一起
TwoThread.java, shown in Listing 2.1.
Listing 2.1 TwoThread.java—The Complete Code for the TwoThread Example
Java代碼
- 1: public class TwoThread extends Thread {
- 2: public void run() {
- 3: for ( int i = 0; i < 10; i++ ) {
- 4: System.out.println(“New thread”);
- 5: }
- 6: }
- 7:
- 8: public static void main(String[] args) {
- 9: TwoThread tt = new TwoThread();
- 10: tt.start();
- 11:
- 12: for ( int i = 0; i < 10; i++ ) {
- 13: System.out.println(“Main thread”);
- 14: }
- 15: }
- 16: }
1: public class TwoThread extends Thread {2: public void run() {3: for ( int i = 0; i < 10; i++ ) {4: System.out.println(“New thread”);5: }6: }7:8: public static void main(String[] args) {9: TwoThread tt = new TwoThread();10: tt.start();11:12: for ( int i = 0; i < 10; i++ ) {13: System.out.println(“Main thread”);14: }15: }16: }
第一次啟動並執行結果:
Main Thread
New Thread
Main Thread
New Thread
Main Thread
New Thread
Main Thread
New Thread
Main Thread
New Thread
Main Thread
New Thread
Main Thread
New Thread
Main Thread
New Thread
Main Thread
New Thread
Main Thread
New Thread
第二次啟動並執行結果:
Main Thread
Main Thread
Main Thread
Main Thread
Main Thread
Main Thread
Main Thread
Main Thread
Main Thread
Main Thread
New Thread
New Thread
New Thread
New Thread
New Thread
New Thread
New Thread
New Thread
New Thread
New Thread
從以上結果可以看出來,每次啟動並執行結果都不一樣,你的機器上也可能不一樣。