多線程(二)java中線程的實現方式--Thread類與Runnable接口

在Java中如果要想實現多線程,有兩種方式可以,一種是繼承Tread類,另外一種則是實現Runnable接口。下面來仔細介紹這兩種實現方式。

啓動一個線程,你簡單的調用run方法是沒有這樣效果的,所以你必須調用Thread類的start方法來啓動你的線程.所以你啓動線程有兩種方法

一是寫一個類繼承自Thread類,然後重寫裏面的run方法,用start方法啓動線程

二是寫一個類實現Runnable接口,實現裏面的run方法,用new Thread(Runnable target).start()方法來啓動

爲啥一定要調用start()方法來啓動來啓動呢?

  因爲線程的運行需要本機操作系統支持。首先我們來看一下star()方法在Thread類中的定義

代碼:start()方法部分定義

public synchronized void start(){
    if (threadStatus != 0)
        throw new IllegalThreadStateException();
    ...
    start0();
    ...
}
private native void start0();
從以上代碼中可以發現,在一個類中的start()方法調用時可能會拋出"IllegalThreadStateException”異常,一般在重複調用star()方法時會拋出這樣異常。實際上此處真正調用的是start0()方法,且此方法在聲明出使用了native關鍵字聲明,此關鍵字表示調用本機的操作系統函數,因爲多線程的實現需要依靠底層操作系統支持的。

1.繼承Thread類

Thread類是在java.lang包中定義的,一個類只要繼承了Thread類,則必須重寫Threa類中的run()方法,此方法爲線程的主體。線程類的定義如下。

public class Main {

    public static void main(String[] args) {
        MyThread T1 = new MyThread("A");
        MyThread T2 = new MyThread("B");
        T1.start();
        T2.start();


    }

}

 class MyThread extends Thread {
    private String name;
    public MyThread(String name) {
        this.name = name;
    }
    
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(name+":"+i);
            try {
                sleep(1000); //休眠1秒,避免太快導致看不到同時執行
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
    }
}

運行結果:

A:0
B:0
A:1
B:1
B:2
A:2
B:3
A:3
A:4
B:4
程序啓動運行main時候,java虛擬機啓動一個進程,主線程main在main()調用時候被創建。隨着調用MitiSay的兩個對象的start方法,另外兩個線程也啓動了,這樣,整個應用就在多線程下運行。
注意:start()方法的調用後並不是立即執行多線程代碼,而是使得該線程變爲可運行態(Runnable),什麼時候運行是由操作系統決定的。
從程序運行的結果可以發現,多線程程序是亂序執行。因此,只有亂序執行的代碼纔有必要設計爲多線程。
Thread.sleep()方法調用目的是不讓當前線程獨自霸佔該進程所獲取的CPU資源,以留出一定時間給其他線程執行的機會。
實際上所有的多線程代碼執行順序都是不確定的,每次執行的結果都是隨機的。(要看效果要去除sleep方法,然後加大打印次數)

2.實現Runnable接口

如果一個類繼承Thread,則不適合資源共享。但是如果實現了Runable接口的話,則很容易的實現資源共享。

public class Main {
    public static void main(String[] args) {
        //測試Runnable
        MyThread1 t1 = new MyThread1();
        new Thread(t1).start();//同一個t1,如果在Thread中就不行,會報錯
        new Thread(t1).start();
        new Thread(t1).start();
    }
}
class MyThread1 implements Runnable{
    private int ticket = 10;
    @Override
    //記得要資源公共,要在run方法之前加上synchronized關鍵字,要不然會出現搶資源的情況
    public synchronized  void  run() {
        for (int i = 0; i <10; i++) {
            if (this.ticket>0) {
                System.out.println("賣票:ticket"+this.ticket--);
            }
        }    
    }  
}

結果:

賣票:ticket10
賣票:ticket9
賣票:ticket8
賣票:ticket7
賣票:ticket6
賣票:ticket5
賣票:ticket4
賣票:ticket3
賣票:ticket2
賣票:ticket1

這裏要注意每個線程都是用同一個實例化對象,如果不是同一個,效果就和上面的一樣了!

總結:

實現Runnable接口比繼承Thread類所具有的優勢:

1):適合多個相同的程序代碼的線程去處理同一個資源

2):可以避免java中的單繼承的限制

3):增加程序的健壯性,代碼可以被多個線程共享,代碼和數據獨立

Runnable接口和Thread類之間的聯繫

public class Thread extends Object implements Runnable
發現Thread類也是Runnable接口的子類

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章