創建線程的兩種方法


創建線程的兩種方法:

  1、繼承Thread類,並重寫Thread類的run方法

  2、實現接口Runnable的run方法。

  注意:另外還有一種創建線程的方法筆者沒有指出,使用線程池方式—Callable接口。Callable接口實現類,call方法可拋出異常、返回線程任務執行完畢后的結果

  面試問題:Thread類直接調用run()和調用start()方法的區別是什么呢?

    答:Thread的start()方法才能創建一個新的線程。如果直接調用run()方法,相當於直接調用一個類中的方法,不會創建一個新的線程。

1.5 創建線程方式一繼承Thread類
創建線程的步驟:
1 定義一個類繼承Thread。
2 重寫run方法。
3 創建子類對象,就是創建線程對象。
4 調用start方法,開啟線程並讓線程執行,同時還會告訴jvm去調用run方法。
 測試類

 1 public class Demo01 {
 2     public static void main(String[] args) {
 3         //創建自定義線程對象
 4         MyThread mt = new MyThread("新的線程!");
 5         //開啟新線程
 6         mt.start();
 7         //在主方法中執行for循環
 8         for (int i = 0; i < 10; i++) {
 9             System.out.println("main線程!"+i);
10         }
11     }
12 }

 自定義線程類

public class MyThread extends Thread {
    //定義指定線程名稱的構造方法
    public MyThread(String name) {
        //調用父類的String參數的構造方法,指定線程的名稱
        super(name);
    }
    /**
     * 重寫run方法,完成該線程執行的邏輯
     */
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName()+":正在執行!"+i);
        }
    }
}

思考:線程對象調用 run方法和調用start方法區別?
線程對象調用run方法不開啟線程。僅是對象調用方法。線程對象調用start開啟線程,並讓jvm調用run方法在開啟的線程中執行。
1.5.1 繼承Thread類原理
我們為什么要繼承Thread類,並調用其的start方法才能開啟線程呢?
繼承Thread類:因為Thread類用來描述線程,具備線程應該有功能。那為什么不直接創建Thread類的對象呢?如下代碼:
Thread t1 = new Thread();
t1.start();//這樣做沒有錯,但是該start調用的是Thread類中的run方法,而這個run方法沒有做什么事情,更重要的是這個run方法中並沒有定義我們需要讓線程執行的代碼。

創建線程的目的是什么?
是為了建立程序單獨的執行路徑,讓多部分代碼實現同時執行。也就是說線程創建並執行需要給定線程要執行的任務。
對於之前所講的主線程,它的任務定義在main函數中。自定義線程需要執行的任務都定義在run方法中。
Thread類run方法中的任務並不是我們所需要的,只有重寫這個run方法。既然Thread類已經定義了線程任務的編寫位置(run方法),那么只要在編寫位置(run方法)中定義任務代碼即可。所以進行了重寫run方法動作。
1.5.2 多線程的內存圖解
多線程執行時,到底在內存中是如何運行的呢?
以上個程序為例,進行圖解說明:
多線程執行時,在棧內存中,其實每一個執行線程都有一片自己所屬的棧內存空間。進行方法的壓棧和彈棧。

當執行線程的任務結束了,線程自動在棧內存中釋放了。但是當所有的執行線程都結束了,那么進程就結束了。
1.5.3 獲取線程名稱
開啟的線程都會有自己的獨立運行棧內存,那么這些運行的線程的名字是什么呢?該如何獲取呢?既然是線程的名字,按照面向對象的特點,是哪個對象的屬性和誰的功能,那么我們就去找那個對象就可以了。查閱Thread類的API文檔發現有個方法是獲取當前正在運行的線程對象。還有個方法是獲取當前線程對象的名稱。既然找到了,我們就可以試試。

 Thread.currentThread()獲取當前線程對象
 Thread.currentThread().getName();獲取當前線程對象的名稱

 1 class MyThread extends Thread {  //繼承Thread
 2     MyThread(String name){
 3         super(name);
 4     }
 5     //復寫其中的run方法
 6     public void run(){
 7         for (int i=1;i<=20 ;i++ ){
 8             System.out.println(Thread.currentThread().getName()+",i="+i);
 9         }
10     }
11 }
12 class ThreadDemo {
13     public static void main(String[] args)     {
14         //創建兩個線程任務
15         MyThread d = new MyThread();
16         MyThread d2 = new MyThread();
17         d.run();//沒有開啟新線程, 在主線程調用run方法
18         d2.start();//開啟一個新線程,新線程調用run方法
19     }
20 }

通過結果觀察,原來主線程的名稱:main;自定義的線程:Thread-0,線程多個時,數字順延。如Thread-1......
進行多線程編程時,不要忘記了Java程序運行是從主線程開始,main方法就是主線程的線程執行內容。
1.6 創建線程方式—實現Runnable接口
創建線程的另一種方法是聲明實現 Runnable 接口的類。該類然后實現 run 方法。然后創建Runnable的子類對象,傳入到某個線程的構造方法中,開啟線程。
為何要實現Runnable接口,Runable是啥玩意呢?繼續API搜索。
查看Runnable接口說明文檔:Runnable接口用來指定每個線程要執行的任務。包含了一個 run 的無參數抽象方法,需要由接口實現類重寫該方法。

 接口中的方法

 Thread類構造方法

創建線程的步驟。
1、定義類實現Runnable接口。
2、覆蓋接口中的run方法。。
3、創建Thread類的對象
4、將Runnable接口的子類對象作為參數傳遞給Thread類的構造函數。
5、調用Thread類的start方法開啟線程。
 代碼演示:

 1 public class Demo02 {
 2     public static void main(String[] args) {
 3         //創建線程執行目標類對象
 4         Runnable runn = new MyRunnable();
 5         //將Runnable接口的子類對象作為參數傳遞給Thread類的構造函數
 6         Thread thread = new Thread(runn);
 7         Thread thread2 = new Thread(runn);
 8         //開啟線程
 9         thread.start();
10         thread2.start();
11         for (int i = 0; i < 10; i++) {
12             System.out.println("main線程:正在執行!"+i);
13         }
14     }
15 }

 自定義線程執行任務類

 1 public class MyRunnable implements Runnable{
 2 
 3     //定義線程要執行的run方法邏輯
 4     @Override
 5     public void run() {
 6         
 7         for (int i = 0; i < 10; i++) {
 8             System.out.println("我的線程:正在執行!"+i);
 9         }
10     }
11 }

 

1.6.1 實現Runnable的原理
為什么需要定一個類去實現Runnable接口呢?繼承Thread類和實現Runnable接口有啥區別呢?
實現Runnable接口,避免了繼承Thread類的單繼承局限性。覆蓋Runnable接口中的run方法,將線程任務代碼定義到run方法中。
創建Thread類的對象,只有創建Thread類的對象才可以創建線程。線程任務已被封裝到Runnable接口的run方法中,而這個run方法所屬於Runnable接口的子類對象,所以將這個子類對象作為參數傳遞給Thread的構造函數,這樣,線程對象創建時就可以明確要運行的線程的任務。
1.6.2 實現Runnable的好處
第二種方式實現Runnable接口避免了單繼承的局限性,所以較為常用。實現Runnable接口的方式,更加的符合面向對象,線程分為兩部分,一部分線程對象,一部分線程任務。繼承Thread類,線程對象和線程任務耦合在一起。一旦創建Thread類的子類對象,既是線程對象,有又有線程任務。實現runnable接口,將線程任務單獨分離出來封裝成對象,類型就是Runnable接口類型。Runnable接口對線程對象和線程任務進行解耦。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM