Java多线程的实现方式-Thread 类,Runnable 接口
在 Java 的 JDK 開發包中,已經自帶了對多線程技術的支持,可以方便地進行多線程編程。實現多線程編程的方式主要有兩種:一種是繼承 Thread 類,另一種是實現 Runnable 接口。下面詳細介紹這兩種具體實現方式。
繼承 Thread 類
在學習如何實現多線程前,先來看看 Thread 類的結構,如下:
public class Thread implements Runnable從上面的源代碼可以發現,Thread 類實現了 Runnable 接口,它們之間具有多態關系。
其實,使用繼承 Thread 類的方式實現多線程,最大的局限就是不支持多繼承,因為 Java 語言的特點就是單根繼承,所以為了支持多繼承,完全可以實現 Runnable 接口的方式,一邊實現一邊繼承。但用這兩種方式創建的線程在工作時的性質是一樣的,沒有本質的區別。
Thread 類有如下兩個常用構造方法:
public Thread(String threadName) public Thread()繼承 Thread 類實現線程的語法格式如下:
public class NewThreadName extends Thread { //NewThreadName 類繼承自 Thread 類public void run(){//線程的執行代碼在這里} }線程實現的業務代碼需要放到 run() 方法中。當一個類繼承 Thread 類后,就可以在該類中覆蓋 run() 方法,將實現線程功能的代碼寫入 run() 方法中,然后同時調用 Thread 類的 start() 方法執行線程,也就是調用 run() 方法。
Thread 對象需要一個任務來執行,任務是指線程在啟動時執行的工作,該工作的功能代碼被寫在 run() 方法中。當執行一個線程程序時,就會自動產生一個線程,主方法正是在這個線程上運行的。當不再啟動其他線程時,該程序就為單線程程序。主方法線程啟動由 Java 虛擬機負責,開發人員負責啟動自己的線程。
如下代碼演示了如何啟動一個線程:
new NewThreadName().start(); //NewThreadName 為繼承自 Thread 的子類注意:如果 start() 方法調用一個已經啟動的線程,系統將會拋出 IllegalThreadStateException 異常。
例 1
編寫一個 Java 程序演示線程的基本使用方法。這里創建的自定義線程類為 MyThread,此類繼承自 Thread,并在重寫的 run() 中輸出一行字符串。
MyThread 類代碼如下:
public class MyThread extends Thread {@Overridepublic void run(){super.run();System.out.println("這是線程類 MyThread");} }接下來編寫啟動 MyThread 線程的主方法,代碼如下:
public static void main(String[] args) {MyThread mythread=new MyThread(); //創建一個線程類mythread.start(); //開啟線程System.out.println("運行結束!"); //在主線程中輸出一個字符串 }運行上面的程序將看到如下所示的運行效果。
運行結束!
這是線程類 MyThread
從上面的運行結果來看,MyThread 類中 run() 方法執行的時間要比主線程晚。這也說明在使用多線程技術時,代碼的運行結果與代碼執行順序或調用順序是無關的。同時也驗證了線程是一個子任務,CPU 以不確定的方式,或者說以隨機的時間來調用線程中的 run() 方法,所以就會出現先打印“運行結束!”,后輸出“這是線程類MyThread”這樣的結果了。
例 2
上面介紹了線程的調用具有隨機性,為了更好地理解這種隨機性這里編寫了一個案例進行演示。
(1) 首先創建自定義的線程類 MyThread01,代碼如下:
public class MyThread extends Thread {public void run(){try {for(int i=0;i<10;i++){int time=(int) (Math.random()*1000);Thread.sleep(time);System.out.println("當前線程名稱:"+Thread.currentThread().getName());}} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}} }(2) 接下來編寫主線程代碼,在這里除了啟動上面的 MyThread01 線程外,還實現了 MyThread01 線程相同的功能。主線程的代碼如下:
public class Test {public static void main(String[] args){ try{ MyThread thread=new MyThread(); thread.setName("myThread"); thread.start(); for (int i=0;i<10;i++){ int time=(int)(Math.random()*1000); Thread.sleep(time); System.out.println("主線程名稱="+Thread.currentThread().getName()); } }catch(InterruptedException e){ e.printStackTrace(); }}}在上述代碼中,為了展現出線程具有隨機特性,所以使用隨機數的形式來使線程得到掛起的效果,從而表現出 CPU 執行哪個線程具有不確定性。
MyThread類中的 start() 方法通知“線程規劃器”此線程已經準備就緒,等待調用線程對象的 run() 方法。這個過程其實就是讓系統安排一個時間來調用 Thread 中的 run() 方法,也就是使線程得到運行,啟動線程,具有異步執行的效果。
如果調用代碼 thread.run() 就不是異步執行了,而是同步,那么此線程對象并不交給“線程規劃器”來進行處理,而是由 main 主線程來調用 run() 方法,也就是必須等 run() 方法中的代碼執行完后才可以執行后面的代碼。
這種采用隨機數延時調用線程的方法又稱為異步調用,程序運行的效果如下所示。
當前線程名稱:myThread 主線程名稱=main 主線程名稱=main 主線程名稱=main 當前線程名稱:myThread 主線程名稱=main 主線程名稱=main 當前線程名稱:myThread 當前線程名稱:myThread 主線程名稱=main 當前線程名稱:myThread 主線程名稱=main 當前線程名稱:myThread 主線程名稱=main 當前線程名稱:myThread 主線程名稱=main 當前線程名稱:myThread 主線程名稱=main 當前線程名稱:myThread 當前線程名稱:myThread例 3
除了異步調用之外,同步執行線程 start() 方法的順序不代表線程啟動的順序。下面創建一個案例演示同步線程的調用。
(1) 首先創建自定義的線程類 MyThread,代碼如下:
public class MyThread extends Thread {private int i;public MyThread(int i){super();this.i=i;}public void run(){System.out.println("當前數字:"+i);} }(2) 接下來編寫主線程代碼,在這里創建 10 個線程類 MyThread,并按順序依次調用它們的 start() 方法。主線程的代碼如下:
public class Test {public static void main(String[] args){ MyThread t11=new MyThread(1); MyThread t12=new MyThread(2); MyThread t13=new MyThread(3); MyThread t14=new MyThread(4); MyThread t15=new MyThread(5); MyThread t16=new MyThread(6); MyThread t17=new MyThread(7); MyThread t18=new MyThread(8); MyThread t19=new MyThread(9); MyThread t110=new MyThread(10); t11.start(); t12.start(); t13.start(); t14.start(); t15.start(); t16.start(); t17.start(); t18.start(); t19.start(); t110.start(); }}程序運行后的結果如下所示,從運行結果中可以看到,雖然調用時數字是有序的,但是由于線程執行的隨機性,導致輸出的數字是無序的,而且每次順序都不一樣。
當前數字:1 當前數字:5 當前數字:4 當前數字:2 當前數字:3 當前數字:7 當前數字:6 當前數字:8 當前數字:9 當前數字:10實現 Runnable 接口
如果要創建的線程類已經有一個父類,這時就不能再繼承 Thread 類,因為 Java 不支持多繼承,所以需要實現 Runnable 接口來應對這樣的情況。
實現 Runnable 接口的語法格式如下:
public class thread extends Object implements Runnable提示:從 JDK 的 API 中可以發現,實質上 Thread 類實現了 Runnable 接口,其中的 run() 方法正是對 Runnable 接口中 run() 方法的具體實現。
實現 Runnable 接口的程序會創建一個 Thread 對象,并將 Runnable 對象與 Thread 對象相關聯。Thread 類有如下兩個與 Runnable 有關的構造方法:
public Thread(Runnable r); public Thread(Runnable r,String name);使用上述兩種構造方法之一均可以將 Runnable 對象與 Thread 實例相關聯。使用 Runnable 接口啟動線程的基本步驟如下。
通過實現 Runnable 接口創建線程時開發人員首先需要編寫一個實現 Runnable 接口的類,然后實例化該類的對象,這樣就創建了 Runnable 對象。接下來使用相應的構造方法創建 Thread 實例。最后使用該實例調用 Thread 類的 start() 方法啟動線程,如圖 1 所示。
例 1
編寫一個簡單的案例演示如何實現 Runnable 接口,以及如何啟動線程。
(1) 首先創建一個自定義的 MyRmmable 類,讓該類實現 Runnable 接口,并在 run() 方法中輸出一個字符串。代碼如下:
public class MyRunnable implements Runnable{@Overridepublic void run() {System.out.println("MyRunnable運行中!");}}(2) 接下來在主線程中編寫代碼,創建一個 MyRunnable 類實例,并將該實例作為參數傳遞給 Thread 類的構造方法,最后調用 Thread 類的 start() 方法啟動線程。具體實現代碼如下:
public class Test {public static void main(String[] args){ Runnable runnable=new MyRunnable();Thread thread=new Thread(runnable);thread.start();System.out.println("主線程運行結束");}}如上述代碼所示,啟動線程的方法非常簡單。運行結果如下所示,同樣驗證了線程執行的隨機性。
主線程運行結束! MyRunnable運行中!注意:要啟動一個新的線程,不是直接調用 Thread 子類對象的 run() 方法,而是調用 Thread 子類的 start() 方法。Thread 類的 start() 方法會產生一個新的線程,該線程用于執行 Thread 子類的 run() 方法。
兩種方法的比較
雖然 Thread 類和 Runnable 接口都可以創建線程,但是它們也都有各自的優缺點。
當一個 run() 方法體現在繼承 Thread 類中時,可以用 this 指向實際控制運行的 Thread 實例。因此,代碼不需要使用以下控制語句:
Thread.currenThread().sleep();不再使用上面的控制語句,而是可以簡單地使用 Threadsleep() 方法,繼承 Thread 類的方式使代碼變得簡單易讀。
從面向對象的角度來看,Thread 類是一個虛擬處理機嚴格的封裝,因此只有當處理機模型修改或擴展時,才應該繼承該類。由于 Java 技術只允許單一繼承,因此如果已經繼承了 Thread 類,就不能再繼承其他任何類,這會使用戶只能采用實現 Runnable 接口的方式創建線程。
Thread和Runnable的區別
如果一個類繼承Thread,則不適合資源共享。但是如果實現了Runable接口的話,則很容易的實現資源共享。
總結:
實現Runnable接口比繼承Thread類所具有的優勢:
1):適合多個相同的程序代碼的線程去處理同一個資源
2):可以避免java中的單繼承的限制
3):增加程序的健壯性,代碼可以被多個線程共享,代碼和數據獨立
4):線程池只能放入實現Runable或callable類線程,不能直接放入繼承Thread的類
提醒一下大家:main方法其實也是一個線程。在java中所以的線程都是同時啟動的,至于什么時候,哪個先執行,完全看誰先得到CPU的資源。
在java中,每次程序運行至少啟動2個線程。一個是main線程,一個是垃圾收集線程。因為每當使用java命令執行一個類的時候,實際上都會啟動一個JVM,每一個jVM實習在就是在操作系統中啟動了一
總結
以上是生活随笔為你收集整理的Java多线程的实现方式-Thread 类,Runnable 接口的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java线程的概念:什么是线程?
- 下一篇: Java线程的生命周期及线程的几种状态