Java中實現多執行緒的兩種方式之間的區別
Java提供了執行緒類Thread來建立多執行緒的程式。其實,建立執行緒與建立普通的類的物件的操作是一樣的,而執行緒就是Thread類或其子類的例項物件。每個Thread物件描述了一個單獨的執行緒。要產生一個執行緒,有兩種方法:
◆需要從Java.lang.Thread類派生一個新的執行緒類,過載它的run()方法;
◆實現Runnalbe介面,過載Runnalbe介面中的run()方法。
為什麼Java要提供兩種方法來建立執行緒呢?它們都有哪些區別?相比而言,哪一種方法更好呢?
在Java中,類僅支援單繼承,也就是說,當定義一個新的類的時候,它只能擴充套件一個外部類.這樣,如果建立自定義執行緒類的時候是通過擴充套件 Thread類的方法來實現的,那麼這個自定義類就不能再去擴充套件其他的類,也就無法實現更加複雜的功能。因此,如果自定義類必須擴充套件其他的類,那麼就可以使用實現Runnable介面的方法來定義該類為執行緒類,這樣就可以避免Java單繼承所帶來的侷限性。
還有一點最重要的就是使用實現Runnable介面的方式建立的執行緒可以處理同一資源,從而實現資源的共享.
1、通過擴充套件Thread類來建立多執行緒
假設一個影院有三個售票口,分別用於向兒童、成人和老人售票。影院為每個視窗放有100張電影票,分別是兒童票、成人票和老人票。三個視窗需要同時賣票,而現在只有一個售票員,這個售票員就相當於一個CPU,三個視窗就相當於三個執行緒。通過程式來看一看是如何建立這三個執行緒的。
- public class MutliThreadDemo {
-
public static void main(String[] args) {
- MutliThread m1=new MutliThread("Window 1");
- MutliThread m2=new MutliThread("Window 2");
- MutliThread m3=new MutliThread("Window 3");
- m1.start();
- m2.start();
- m3.start();
- }
- }
- public class MutliThread extends Thread {
-
private int ticket
- public MutliThread (){}
- public MutliThread (String name){
- super(name);
- }
- @Override
- public void run() {
- while(ticket>0){
- System.out.println(ticket--+" is saled by "+Thread.currentThread().getName());
- }
- }
- }
程式中定義一個執行緒類,它擴充套件了Thread類。利用擴充套件的執行緒類在MutliThreadDemo類的主方法中建立了三個執行緒物件,並通過start()方法分別將它們啟動。
從結果可以看到,每個執行緒分別對應100張電影票,之間並無任何關係,這就說明每個執行緒之間是平等的,沒有優先順序關係,因此都有機會得到CPU的處理。但是結果
顯示這三個執行緒並不是依次交替執行,而是在三個執行緒同時被執行的情況下,有的執行緒被分配時間片的機會多,票被提前賣完,而有的執行緒被分配時間片的機會比較
少,票遲一些賣完。
可見,利用擴充套件Thread類建立的多個執行緒,雖然執行的是相同的程式碼,但彼此相互獨立,且各自擁有自己的資源,互不干擾。
2、通過實現Runnable介面來建立多執行緒
- public class MutliThreadDemo {
- public static void main(String[] args) {
- MutliThread m1=new MutliThread("Window 1");
- MutliThread m2=new MutliThread("Window 2");
- MutliThread m3=new MutliThread("Window 3");
- Thread t1=new Thread(m1);
- Thread t2=new Thread(m2);
- Thread t3=new Thread(m3);
- t1.start();
- t2.start();
- t3.start();
- }
- }
- public class MutliThread implements Runnable{
- private int ticket=100;//每個執行緒都擁有100張票
- private String name;
- MutliThread(String name){
- this.name=name;
- }
- public void run(){
- while(ticket>0){
- System.out.println(ticket--+" is saled by "+name);
- }
- }
- }
由於這三個執行緒也是彼此獨立,各自擁有自己的資源,即100張電影票,因此程式輸出的結果和 1 結果大同小異。均是各自執行緒對自己的100張票進行單獨的處理,互不影響。
可見,只要現實的情況要求保證新建執行緒彼此相互獨立,各自擁有資源,且互不干擾,採用哪個方式來建立多執行緒都是可以的。因為這兩種方式建立的多執行緒程式能夠實現相同的功能。
3、通過實現Runnable介面來實現執行緒間的資源共享
現實中也存在這樣的情況,比如模擬一個火車站的售票系統,假如當日從A地發往B地的火車票只有100張,且允許所有視窗賣這100張票,那麼每一個視窗也相當於一個執行緒,但是這時和前面的例子不同之處就在於所有執行緒處理的資源是同一個資源,即100張車票。如果還用前面的方式來建立執行緒顯然是無法實現的,這種情況該怎樣處理呢?看下面這個程式,程式程式碼如下所示:
- public class MutliThreadDemo {
- public static void main(String[] args) {
- MutliThread m=new MutliThread();
- Thread t1=new Thread(m);
- Thread t2=new Thread(m);
- Thread t3=new Thread(m);
- t1.start();
- t2.start();
- t3.start();
- }
- }
- public class MutliThread implements Runnable{
- private int ticket=100;//每個執行緒都擁有100張票
- public void run(){
- while(ticket>0){
- System.out.println(ticket--+" is saled by "+Thread.currentThread());
- }
- }
- }
結果正如前面分析的那樣,程式在記憶體中僅建立了一個資源,而新建的三個執行緒都是基於訪問這同一資源的,並且由於每個執行緒上所執行的是相同的程式碼,因此它們執行的功能也是相同的。
可見,如果現實問題中要求必須建立多個執行緒來執行同一任務,而且這多個執行緒之間還將共享同一個資源,那麼就可以使用實現Runnable介面的方式來建立多執行緒程式。而這一功能通過擴充套件Thread類是無法實現的,讀者想想看,為什麼?
實現Runnable介面相對於擴充套件Thread類來說,具有無可比擬的優勢。這種方式不僅有利於程式的健壯性,使程式碼能夠被多個執行緒共享,而且程式碼和資料資源相對獨立,從而特別適合多個具有相同程式碼的執行緒去處理同一資源的情況。這樣一來,執行緒、程式碼和資料資源三者有效分離,很好地體現了面向物件程式設計的思想。因此,幾乎所有的多執行緒程式都是通過實現Runnable介面的方式來完成的。