1. 多線程
1.1 創(chuàng)建線程類
在Java中可以簡(jiǎn)單的從Thread類中繼承創(chuàng)建自己的線程類:
public class MyFirstThread extends Thread {
public void run() { . . .}
}
說明:
(1) Thread類位是java.lang包中,所以可以不用顯示import;
(2) 從Thread類中繼承下來的類最好重載run()方法,以運(yùn)行需要的代碼;
可以按以下方法實(shí)例化并運(yùn)行線程:
MyFirstThread aMFT = new MyFirstThread();
aMFT.start();
說明:
(3) 實(shí)例化線程類后,系統(tǒng)會(huì)初始化一些參數(shù),主要是為線程創(chuàng)建名稱,把新的線程加入指定的線程組,初始化線程運(yùn)行需要的內(nèi)存空間,指定新線程的優(yōu)先級(jí)別,指定它的守候線程;
(4) start方法是Thread類中的方法,它會(huì)調(diào)用run方法,在新的線程中運(yùn)行指定的代碼;
(5) 除了start方法外,從Thread繼承下來的類還具有其它一些主要的方法:stop,suspend,resume等;
以下是一個(gè)完整的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接口
創(chuàng)建多線程運(yùn)行指定代碼的另一種方法是,在創(chuàng)建類時(shí)implement Runable這個(gè)接口:
public class MySecondThread extends ImportantClass implements Runnable {
public void run() {. . .}
}
說明:
(1) 該類implement Runable接口,就表明有意圖運(yùn)行在單獨(dú)的線程中,Thread也是implement Runable接口的;
(2) Implement Runalbe接口至少需要實(shí)現(xiàn)run方法;
以下是創(chuàng)建新線程運(yùn)行該類的實(shí)例:
MySecondThread aMST = new MySecondThread();
Thread aThread = new Thread(aMST);
aThread.start();
說明:
(3) Thread類有多個(gè)構(gòu)造函數(shù)Thread()、Thread(Runable target)等,本例中用的就是第二個(gè)構(gòu)造函數(shù),它有一個(gè)Runable類型的函數(shù),所以要在多線程中運(yùn)行的實(shí)例的類必須是implement Runable的;
(4) AThead.start()方法調(diào)用Thread實(shí)例的中的target.run方法,本例中就是MySecondThread實(shí)例中的run方法;
(5) Thread構(gòu)造函數(shù)還可以指定線程名,運(yùn)行所需的stack,線程所屬的組等;
為了防止線程非正常結(jié)束,需要將start方法置入try…catch中,如:
try{
myThread.start();
}catch(ThreadDeath aTD){
System.out.println("end Thread");
throw aTD;
}
在這個(gè)例子中將捕獲ThreadDeath異常,處理后重新拋出該異常,以便Java執(zhí)行stop方法,進(jìn)行資源等清理工作。
1.3 線程的優(yōu)先級(jí)
多個(gè)線程的執(zhí)行是有一定的優(yōu)先級(jí)別的,對(duì)于下面這個(gè)例子:
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();
}}
對(duì)于非搶占式的系統(tǒng),上例中的第一個(gè)線程會(huì)一直運(yùn)行,第二個(gè)線程沒有機(jī)會(huì)運(yùn)行;對(duì)于搶占式的系統(tǒng),這二人線程會(huì)交替運(yùn)行。
為了讓多線程在非搶占式中運(yùn)行,最好在run方法中加入以下語(yǔ)句:
Thread.yield()
即
public void run() {
while (true)
System.out.println(Thread.currentThread().getName());
Thread.yield()
}
Thread.yield會(huì)將當(dāng)前線程暫時(shí)讓位一小段時(shí)間,讓其它的線程有機(jī)會(huì)運(yùn)行,過了這段時(shí)間后,該線程繼承運(yùn)行。上述功能也可以用Thread.sleep()方法實(shí)現(xiàn)。
在Java中有優(yōu)先級(jí)別可以從1到10,其中1可以用Thread.MIN_PRIORITY表示,5可以用Thread.NORM_PRIORITY,10可以用Thread.MAX_PRIORITY表示,新建一個(gè)線程默認(rèn)的級(jí)別是Thread.NORM_PRIORITY。也可以使用setPriority方法改變線程的優(yōu)先級(jí)別,如T1.setPriority(T2.getPriority + 1)。
與yield方法相反的是join方法,它表示一直要等到指定的線程運(yùn)行完畢,如:
try{ t.join();} catch (InterruptedException ignored) { }
表示要等到線程t運(yùn)行完畢后,再執(zhí)行下一步操作。這種情況比較少見。
1.4 synchronized
為了保證某個(gè)方法或者對(duì)象某個(gè)時(shí)刻只能被一個(gè)方法訪問,那就需要使用synchronized關(guān)鍵字。
如:
public synchronized void countMe() {
crucialValue += 1;
}
就表示countMe這個(gè)方法中的操作是一個(gè)原子操作,+= 要執(zhí)行三個(gè)步驟,使用synchronized后,這三個(gè)步驟是具有原子性,即在三個(gè)步驟完成前,其它對(duì)于crucialValue的訪問都將被拒絕,即可保證countMe的線程安全。
另一個(gè)例子:
synchronized(p) {
safeX = p.x();
safeY = p.y();
}
表示在block范圍內(nèi)鎖定p對(duì)象,不許其它程序修改p對(duì)象中的值。
以上代碼的作用都是保護(hù)某個(gè)對(duì)象內(nèi)的變量不能同時(shí)被多個(gè)線程訪問,下面介紹如何保護(hù)class variable的線程安全:
public class StaticCounter {
private static int crucialValue;
public void countMe() {
synchronized(getClass()) {
crucialValue += 1;
} } }
說明:
(1) 在這個(gè)例子中,crucialValue是private并且static,這表示它可以被該類的所有實(shí)例訪問;
(2) synchronized使用getClass方法獲取類名,而不能直接使用StaticCounter
(3) 如果crucialValue是public的,那么修改代碼成:
synchronized(Class.forName("StaticCounter")) {
StaticCounter.crucialValue += 1;
}