一、Java有兩種方式實現多線程,第一個是繼承Thread類,第二個是實現Runnable接口。他們之間的聯系:
1、Thread類實現了Runable接口。
2、都需要重寫里面Run方法。
二、實現Runnable接口相對於繼承Thread類來說,有如下顯著的好處:
1、適合多個相同程序代碼的線程去處理同一資源的情況,把虛擬CPU(線程)同程序的代碼,數據有效的分離,較好地體現了面向對象的設計思想。
2、可以避免由於Java的單繼承特性帶來的局限。我們經常碰到這樣一種情況,即當我們要將已經繼承了某一個類的子類放入多線程中,由於一個類不能同時有兩個父類,所以不能用繼承Thread類的方式,那么,這個類就只能采用實現Runnable接口的方式了。
3、有利於程序的健壯性,代碼能夠被多個線程共享,代碼與數據是獨立的。當多個線程的執行代碼來自同一個類的實例時,即稱它們共享相同的代碼。多個線程操作相同的數據,與它們的代碼無關。當共享訪問相同的對象是,即它們共享相同的數據。當線程被構造時,需要的代碼和數據通過一個對象作為構造函數實參傳遞進去,這個對象就是一個實現了Runnable接口的類的實例。
看一下以繼承Thread的賣票例子:
class MyThread extends Thread{
private int ticket = 5;
public void run(){
while(true){
System.out.println("Thread ticket = " + ticket--);
if(ticket < 0){
break;
}
}
}
}
public static void main(String[] args) {
new MyThread().start();
new MyThread().start();
}
輸出結果:
Thread ticket = 5
Thread ticket = 5
Thread ticket = 4
Thread ticket = 4
Thread ticket = 3
Thread ticket = 2
Thread ticket = 3
Thread ticket = 1
Thread ticket = 2
Thread ticket = 0
Thread ticket = 1
Thread ticket = 0
從結果可以看出:創建了兩個多線程對象,他們分別實現了買票任務;
實現Runnable接口的賣票例子:
class MyThread2 implements Runnable{
private int ticket = 5;
public void run(){
while(true){
System.out.println("Runnable ticket = " + ticket--);
if(ticket < 0){
break;
}
}
}
}
<pre name="code" class="html">public static void main(String[] args) {
MyThread2 m=new MyThread2();
new Thread(m).start();
new Thread(m).start();
}
輸出結果:
1 Runnable ticket = 5 2 Runnable ticket = 4 3 Runnable ticket = 3 4 Runnable ticket = 2 5 Runnable ticket = 1 6 Runnable ticket = 0
從結果可以看出雖然我們聲明了兩個線程,但是一共賣了6張票。他們實現了資源共享。PS:在實際開發中,一定要注意命名規范,其次上面實現Runable接口的例子由於同時操作一個資源,會出現線程不安全的情況,如果情況需要,我們需要進行同步操作。
三、線程多次調用start方法,引發的異常
通過調用Thread類的start()方法來啟動一個線程,這時此線程是處於就緒狀態,並沒有運行。然后通過此Thread類調用方法run()來完成其運行操作的,這里方法run()稱為線程體,它包含了要執行的這個線程的內容,Run方法運行結束,此線程終止,而CPU再運行其它線程,而如果直接用Run方法,這只是調用一個方法而已,程序中依然只有主線程--這一個線程,其程序執行路徑還是只有一條,這樣就沒有達到寫線程的目的。
一個線程對象只能調用一次start方法.從new到等待運行是單行道,所以如果你對一個已經啟動的線程對象再調用一次start方法的話,會產生:IllegalThreadStateException異常.
可以被重復調用的是run()方法。
Thread類中run()和start()方法的區別如下:
run()方法:在本線程內調用該Runnable對象的run()方法,可以重復多次調用;
start()方法:啟動一個線程,調用該Runnable對象的run()方法,不能多次啟動一個線程;