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派生類:
java 代碼
public class ComplexThread extends Thread {
private int delay;
ComplexThread(String name, float seconds) {
super(name);
delay = (int) seconds * 1000; // delays are in milliseconds
start(); // start up ourself!
}
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
return;
}
}
public static void main(String argv[]) {
new ComplexThread("one potato", 1.1F);
new ComplexThread("two potato", 1.3F);
new ComplexThread("three potato", 0.5F);
new ComplexThread("four", 0.7F);
} }
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() {
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;