創建方式
- 繼承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創建完本地操作系統級線程后回調的方法,不可以手動調用(否則就是普通方法)