1、採用繼承Thread類建立線程
定義一個java.lang.Thread類的子類,並重寫其run方法:
class MyThread extends Thread{
public void run() { ……}
}
然後產生該類的對象:MyThread myThread = new MyThread(…);
2、通過實現Runnable介面建立線程
定義實現Runnable介面的線程類
class MyThread implements Runnable{
public void run() {……}
}
然後再利用Thread類構造線程類:
Thread myThread = new Thread (target);
Runnable中只有一個方法:public void run(); 用以定義線程運行體;
線程的啟動:myThread.start(); 3、Thread類的構造方法:
Thread ()
Thread (Runnable target)
Thread (Runnable target, String name)
Thread (String name)
Thread (ThreadGroup group, Runnable target)
Thread (ThreadGroup group, Runnable target, String name)
Thread (ThreadGroup group, String name)
group — 指明該線程所屬的線程組;
target — 提供線程體的對象,必須實現Runnable介面的run()方法;
name — 線程名稱,每個線程都有自己的名字,若沒有指定,則Java自動給線程賦予惟一的名字;
兩種方式的比較:
使用Runnable介面
可以將CPU,代碼和資料分開,形成清晰的模型;
還可以從其他類繼承;
保持程式風格的一致性;
在實現Runnable介面的類中可以使用靜態方法如:Thread.currentThread()擷取當前線程進行控制;
直接繼承Thread類
不能再從其他類繼承;
編寫簡單,可以直接操縱線程; 4、線程的優先順序
線程的優先順序用數字表示,範圍從1到10,數字越大層級越高,一個線程的預設優先順序是5;
Thread類中定義的靜態成員變數:
Thread.MIN_PRIORITY = 1
Thread.MAX_PRIORITY = 10
Thread.NORM_PRIORITY = 5
建立線程將繼承其父線程的優先順序。一般情況下,主線程(main)具有普通優先順序;
獲得和設定線程對象優先順序的方法:
int getPriority ();
void setPriority (int newPriority);
5、線程的狀態和控制
建立狀態
new運算子建立線程對象;
就緒狀態
調用線程的start()方法啟動線程,使線程處於就緒狀態,此時線程可能還並未真正執行;
運行狀態:
JVM的線程調度管理器選中處於就緒狀態的線程,使其佔有CPU開始運行run()方法中的代碼,直至被終止或阻塞;
阻塞狀態:該狀態的線程不可運行,直至阻塞原因削除,線程轉入就緒狀態,重新進入線程隊列排隊,再次運行從終止處繼續執行;
調用了sleep()方法;
調用了suspend()方法;
為等候一個條件變數,線程調用wait()方法;
輸入/輸出流中發生線程阻塞;
針對上述四種情況,使線程返回就緒狀態的方法:
sleep()方法中的參數為休息時間,單位為毫秒,時間過去後,線程即為就緒的。
一個線程調用suspend()方法後,只能有其它線程調用它的resume()方法恢複。
如果一個線程等待條件變數,如果要停止等待,需要條件變數所在的對象調用notify()或者notifyAll()方法。
特定的I/O指令結束阻塞狀態。
終止狀態:有兩種情況可導致線程終止
自然撤銷,正常啟動並執行線程完成了其全部工作;
調用stop()方法強制終止;
Thread提供了方法isAlive(),如果線程已經啟動,但是未終止,返回true,反之,返回 false,表示該線程未啟動,或者已終止。
如果isAlive()方法返回true,不能區分是就緒狀態、阻塞狀態還是運行狀態。
注意事項:
對於任何狀態,如果調用的方法和狀態不符,都會引起非法狀態處理異常。比如。線程剛建立後,只能調用 start ()或者 stop ()方法,如果調用其它方法就會引起非法狀態處理。
sleep方法
可以調用Thread的靜態方法:
public static void sleep(long mills) throws InterruptedException
樣本:TestInterrupt.java
join方法
合并某個線程,是一種相對原始的線程間通訊形式;
例如:在threadX中執行如下代碼:
try{ threadY.join(); } catch ( InterruptedException e){…}
threadX將阻塞,一直等待threadY的消亡;
樣本:TestJoin.java
yield方法:主動讓出CPU,給同等級或更進階線程執行的機會;
不能濫用yield(),執行線程間的環境切換可能導致過量的系統開銷;1秒鐘內不能超過5次;
樣本:TestYield.java
守護線程(Daemon線程)
具有最低的優先順序,用於在後台為系統中的其他對象和線程提供服務;一般應該是一個獨立的線程,它的run()方法是一個無限迴圈.
將一個使用者線程設定為守護線程的方式是線上程對象啟動之前調用線程對象的setDaemon(boolean on)方法。
可以利用isDaemon()方法來判斷一個線程是否為守護線程;
守護線程守護線程與其它線程的區別是,如果守護線程是唯一運行著的線程,程式會自動結束
典型的守護線程例子是JVM中的系統資源自動回收線程,它始終在低層級的狀態中運行,用於即時監控和管理系統中的可回收資源。 6、Java的線程同步機制:
共用資料的線程互斥鎖定
任何時刻只允許一個線程對共用資料對象進行操作
傳送資料的線程同步機制
對於需要傳送資料的多個線程必須同步運行,步調一致,保證傳送的資料及時準確收到;
每個對象都對應於一個可稱為“互斥鎖”的標記,該標記保證任一時刻,只能有一個線程訪問該對象;
關鍵字synchronized用來表示互斥鎖。當某個對象由synchronized修飾時,表明該對象在任一時刻只能由一個線程訪問;
實現同步的方法例子沒做
通過線程監視器,使用一個共用的條件變數,對線程同步進行更精準的控制
利用wait()、notify()、notifyAll()方法,來保證生產者與消費者的同步:生產一個,消費一個;
多線程同步機制總結:
對於多個線程共用的對象或方法,使用synchronized修飾;
若一個線程必須等待某個對象的狀態改變的話,通過進入同步方法或調用wait()方法實現;
每當一個方法修改共用對象的狀態時,應調用nofity()或notifyAll()進行通知;
死結問題
線程能夠被阻塞,當支援其運行所需的資源被另外一個阻塞線程鎖定時,將會形成所謂的死結(Deadlock);
Java語言自身沒有提供能預防死結的機制
死結可能潛伏在啟動並執行程式中,防止死結是設計階段的一個重要任務;
哲學家吃飯問題;死結樣本:TestDeadLock.java
解決死結問題的方法:給條件變數施加排序;
1. 線程間的通訊可以用管道流.
建立管道流:
PipedInputStream pis = new PipedInputStream();
PipedOutputStream pos = new PipedOutputStream(pis);
或:
PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream(pos);
1. 用過濾流封裝管道流進行讀寫
PrintStream p = new PrintStream( pos );
p.println(“hello”);
DataInputStream d = new DataInputStream(pis);
d.readLine();
2. 通過一個中間類來傳遞資訊.
管道流可以串連兩個線程間的通訊
下面的例子裡有兩個線程在運行,一個往外輸出資訊,一個讀入資訊.
將一個寫線程的輸出通過管道流定義為讀線程的輸入.
outStream = new PipedOutputStream();
inStream = new PipedInputStream(outStream);
new Writer( outStream ).start();
new Reader( inStream ).start();