於Java好的方面是它有內置的寫多線程的支持。Java的設計者知道多線程編程的價值,所以聰明決定在Java的核心部分就決定直接支持線程。在第7章“並發存取對象和變量”就闡述了在Java語言中,synchronized關鍵字如何被用來鎖住對象和類來控制並發存取數據。Thread和 ThreadGroup類就在java.lang包的核心API中。通過wait( ) notify( ) 方法的支持,Java中所有類的父類Object具有線程間通信的能力。即使當前操作系統不支持線程概念,一個寫好的JVM也可以模擬多線程環境。在Java中,線程的支持不是事後的,而是從一開始設計時就包括進來的。
一個簡單的兩個線程例子
概述
這章展示了創建並在一個小的Java應用中運行線程是如何簡單。第一個線程是總是有JVM創建的“main”線程,該線程開始運行程序。這個主線程然後創建第二個線程。每個線程將在控制台打印各自信息,以此來證明他們好像以同步方式運行。
這章中例子創建一個線程的步驟如下:
繼承Java.lang.Thread類
在繼承Thread的子類中重寫run( )方法;
創建這個新類的實例
調用該實例的start( )方法
繼承Java.lang.thread類
在JavaVM中每個線程與Java.lang.Thread類的一個實例想關聯。這些Thread對象作為與底層操作系統線程交互的接口。通過類中的方法,可以對線程進行開始,停止,中斷,命名、設置優先級和查詢有關當前狀態。
注意:有兩種方式可創建一個允許線程運行與其中的類。一種方式是繼承Thread類。另一種是繼承任意類並且實現Runnable接口。為了說明的原因,繼承Thread類是最簡單的方法,本書開始就是用該方法。在實際中,實現Runnable接口工作地更好些。
在這個例子中,創建一個新線程的第一步是繼承Java.lang.Thread類:
Java代碼
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”);
}
}
循環完成之後,線程從run()方法返回,然後安靜死掉。
創建一個新線程 Spawning a New Thread
新線程從已經運行的線程來創建。首先,構造一個新Thread實例。在此例中,一個新TwoThread對象將正好,因為TwoThread IS-A Thread:
TwoThread tt = new TwoThread();
下一步是調用start()方法准備開始執行線程(start()從Thread繼承而來):
tt.start();
start()調用後立即返回,不需要等待其他線程開始執行。在start()中,父線程異步地從JavaVM中給線程調度程序發出信號,告訴它只要它方便,其他線程可以開始執行了。在未來某個不可預期的時間裡,其他線程將被激活,並調用Thread對象的run()方法(在該例中是指 TwoThread中實現的重寫方法run())。與此同時,原來的線程繼續執行start()方法後面的語句。
兩個線程並發、獨立地運行。在一個多處理器的機器上,這兩個線程可能在同一時刻真正都在運行,各自運行在自己的處理器上。
一個更常見的情況是只有一個處理器,JavaVM和操作系統共同工作來短時間調度每個線程。當其他線程被凍結,等待處理器的下一次機會時,每個線程就得到一次運行的機會。這種在線程之間的上下文切換是非常快的,給人一種同時執行的假象。
提示:一個新創建的線程可能在start()被調用之後的任何時間開始執行(進入run()方法)。這意味著start()方法之後的任何語句被執行之前,原來線程都可能被換出(swapped out)。
假如原來的線程正在執行下面的代碼:
stmt1();
tt.start();
新線程有一個run()方法如下:
Java代碼
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 {