線程創建的三種方式及區別


創建方式

  • 繼承Thread類

    (1)定義Thread類的子類,並重寫該類的run方法,該run方法的方法體就代表了線程要完成的任務。因此把run()方法稱為執行體。

  (2)創建Thread子類的實例,即創建了線程對象。

  (3)調用線程對象的start()方法來啟動該線程。

package Thread;

import java.util.concurrent.*;

public class TestThread {
    public static void main(String[] args) throws Exception {
        testExtends();
    }

    public static void testExtends() throws Exception {
        Thread t1 = new MyThreadExtends();
        Thread t2 = new MyThreadExtends();
        t1.start();
        t2.start();
    }
}

class MyThreadExtends extends Thread {
    @Override
    public void run() {
        System.out.println("通過繼承Thread,線程號:" + currentThread().getName());
    }
}

 

  • 實現Runnable接口 

(1)定義runnable接口的實現類,並重寫該接口的run()方法,該run()方法的方法體同樣是該線程的線程執行體。 

(2)創建 Runnable實現類的實例,並依此實例作為Thread的target來創建Thread對象,該Thread對象才是真正的線程對象。 

(3)調用線程對象的start()方法來啟動該線程。

package Thread;

import java.util.concurrent.*;
//測試類
public class TestThread {
    public static void main(String[] args) throws Exception {
         testImplents();
    }

    public static void testImplents() throws Exception {
        MyThreadImplements myThreadImplements = new MyThreadImplements();
        Thread t1 = new Thread(myThreadImplements);
        Thread t2 = new Thread(myThreadImplements, "my thread -2");
        t1.start();
        t2.start();
    }
}
//線程類
class MyThreadImplements implements Runnable {
    @Override
    public void run() {
        System.out.println("通過實現Runable,線程號:" + Thread.currentThread().getName());
    }
}

 

  • 實現Callable接口

(1)創建Callable接口的實現類,並實現call()方法,該call()方法將作為線程執行體,並且有返回值。

(2)創建Callable實現類的實例,使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的返回值。

(3)使用FutureTask對象作為Thread對象的target創建並啟動新線程。

(4)調用FutureTask對象的get()方法來獲得子線程執行結束后的返回值

package Thread;

import java.util.concurrent.*;

public class TestThread {
    public static void main(String[] args) throws Exception {
        testCallable();
    }

    public static void testCallable() throws Exception {
        Callable callable = new MyThreadCallable();
        FutureTask task = new FutureTask(callable);
        new Thread(task).start();
        System.out.println(task.get());
        Thread.sleep(10);//等待線程執行結束
        //task.get() 獲取call()的返回值。若調用時call()方法未返回,則阻塞線程等待返回值
        //get的傳入參數為等待時間,超時拋出超時異常;傳入參數為空時,則不設超時,一直等待
        System.out.println(task.get(100L, TimeUnit.MILLISECONDS));
    }
}

class MyThreadCallable implements Callable {

    @Override
    public Object call() throws Exception {
        System.out.println("通過實現Callable,線程號:" + Thread.currentThread().getName());
        return 10;
    }
}

 

三種方式的優缺點 

  • 采用繼承Thread類方式:

   (1)優點:編寫簡單,如果需要訪問當前線程,無需使用Thread.currentThread()方法,直接使用this,即可獲得當前線程。
   (2)缺點:因為線程類已經繼承了Thread類,所以不能再繼承其他的父類。

  • 采用實現Runnable接口方式:

   (1)優點:線程類只是實現了Runable接口,還可以繼承其他的類。在這種方式下,可以多個線程共享同一個目標對象,所以非常適合多個相同線程來處理同一份資源的情況,從而可以將CPU代碼和數據分開,形成清晰的模型,較好地體現了面向對象的思想。
   (2)缺點:編程稍微復雜,如果需要訪問當前線程,必須使用Thread.currentThread()方法。

  • Runnable和Callable的區別:

   (1)Callable規定的方法是call(),Runnable規定的方法是run().
   (2)Callable的任務執行后可返回值,而Runnable的任務是不能返回值得
   (3)call方法可以拋出異常,run方法不可以,因為run方法本身沒有拋出異常,所以自定義的線程類在重寫run的時候也無法拋出異常
   (4)運行Callable任務可以拿到一個Future對象,表示異步計算的結果。它提供了檢查計算是否完成的方法,以等待計算的完成,並檢索計算的結果。通過Future對象可以了解任務執行情況,可取消任務的執行,還可獲取執行結果。

 start()和run()的區別

  • start()方法用來,開啟線程,但是線程開啟后並沒有立即執行,他需要獲取cpu的執行權才可以執行
  • run()方法是由jvm創建完本地操作系統級線程后回調的方法,不可以手動調用(否則就是普通方法)

 


免責聲明!

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



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