1. 多線程
1.1 創建線程類
在Java中可以簡單的從Thread類中繼承創建自己的線程類:
public class MyFirstThread extends Thread {
public void run() { . . .}
}
說明:
(1) Thread類位是Java.lang包中,所以可以不用顯示import;
(2) 從Thread類中繼承下來的類最好重載run()方法,以運行需要的代碼;
可以按以下方法實例化並運行線程:
MyFirstThread aMFT = new MyFirstThread();
aMFT.start();
說明:
(3) 實例化線程類後,系統會初始化一些參數,主要是為線程創建名稱,把新的線程加入指定的線程組,初始化線程運行需要的內存空間,指定新線程的優先級別,指定它的守候線程;
(4) start方法是Thread類中的方法,它會調用run方法,在新的線程中運行指定的代碼;
(5) 除了start方法外,從Thread繼承下來的類還具有其它一些主要的方法:stop,suspend,resume等;
以下是一個完整的Thread派生類:
1: public class ComplexThread extends Thread {
2: private int delay;
3:
4: ComplexThread(String name, float seconds) {
5: super(name);
6: delay = (int) seconds * 1000; // delays are in milliseconds
7: start(); // start up ourself!
8: }
9:
10: public void run() {
11: while (true) {
12: System.out.println(Thread.currentThread().getName());
13: try {
14: Thread.sleep(delay);
15: } catch (InterruptedException e) {
16: return;
17: }
18: }
19: }
20:
21: public static void main(String argv[]) {
22: new ComplexThread("one potato", 1.1F);
23: new ComplexThread("two potato", 1.3F);
24: new ComplexThread("three potato", 0.5F);
25: new ComplexThread("four", 0.7F);
26: }
27: }
1.2 Runable接口
創建多線程運行指定代碼的另一種方法是,在創建類時implement Runable這個接口:
public class MySecondThread extends ImportantClass implements Runnable {
public void run() {. . .}
}
說明:
(1) 該類implement Runable接口,就表明有意圖運行在單獨的線程中,Thread也是implement Runable接口的;
(2) Implement Runalbe接口至少需要實現run方法;
以下是創建新線程運行該類的實例:
MySecondThread aMST = new MySecondThread();
Thread aThread = new Thread(aMST);
aThread.start();
說明:
(3) Thread類有多個構造函數Thread()、Thread(Runable target)等,本例中用的就是第二個構造函數,它有一個Runable類型的函數,所以要在多線程中運行的實例的類必須是implement Runable的;
(4) AThead.start()方法調用Thread實例的中的target.run方法,本例中就是MySecondThread實例中的run方法;
(5) Thread構造函數還可以指定線程名,運行所需的stack,線程所屬的組等;
為了防止線程非正常結束,需要將start方法置入try…catch中,如:
try{
myThread.start();
}catch(ThreadDeath aTD){
System.out.println("end Thread");
throw aTD;
}
在這個例子中將捕獲ThreadDeath異常,處理後重新拋出該異常,以便Java執行stop方法,進行資源等清理工作。
1.3 線程的優先級
多個線程的執行是有一定的優先級別的,對於下面這個例子:
public class RunnablePotato implements Runnable {
public void run() {
while (true)
System.out.println(Thread.currentThread().getName());
}}
public class PotatoThreadTester {
public static void main(String argv[]) {
RunnablePotato aRP = new RunnablePotato();
Thread T1 = new Thread(aRP, "one potato");
Thread T2 = new Thread(aRP, "two potato");
T1.start();
T2.start();
}}
對於非搶占式的系統,上例中的第一個線程會一直運行,第二個線程沒有機會運行;對於搶占式的系統,這二人線程會交替運行。
為了讓多線程在非搶占式中運行,最好在run方法中加入以下語句:
Thread.yIEld()
即
public void run() {
while (true)
System.out.println(Thread.currentThread().getName());
Thread.yIEld()
}
Thread.yIEld會將當前線程暫時讓位一小段時間,讓其它的線程有機會運行,過了這段時間後,該線程繼承運行。上述功能也可以用Thread.sleep()方法實現。
在Java中有優先級別可以從1到10,其中1可以用Thread.MIN_PRIORITY表示,5可以用Thread.NORM_PRIORITY,10可以用Thread.MAX_PRIORITY表示,新建一個線程默認的級別是Thread.NORM_PRIORITY。也可以使用setPriority方法改變線程的優先級別,如T1.setPriority(T2.getPriority + 1)。
與yIEld方法相反的是join方法,它表示一直要等到指定的線程運行完畢,如:
try{ t.join();} catch (InterruptedException ignored) { }
表示要等到線程t運行完畢後,再執行下一步操作。這種情況比較少見。
1.4 synchronized
為了保證某個方法或者對象某個時刻只能被一個方法訪問,那就需要使用synchronized關鍵字。
如:
public synchronized void countMe() {
crucialValue += 1;
}
就表示countMe這個方法中的操作是一個原子操作,+= 要執行三個步驟,使用synchronized後,這三個步驟是具有原子性,即在三個步驟完成前,其它對於crucialValue的訪問都將被拒絕,即可保證countMe的線程安全。
另一個例子:
synchronized(p) {
safeX = p.x();
safeY = p.y();
}
表示在block范圍內鎖定p對象,不許其它程序修改p對象中的值。
以上代碼的作用都是保護某個對象內的變量不能同時被多個線程訪問,下面介紹如何保護class variable的線程安全:
public class StaticCounter {
private static int crucialValue;
public void countMe() {
synchronized(getClass()) {
crucialValue += 1;
} } }
說明:
(1) 在這個例子中,crucialValue是private並且static,這表示它可以被該類的所有實例訪問;
(2) synchronized使用getClass方法獲取類名,而不能直接使用StaticCounter
(3) 如果crucialValue是public的,那麼修改代碼成:
synchronized(Class.forName("StaticCounter")) {
StaticCounter.crucialValue += 1;
}