第11組 Alpha衝刺 (當前輪次3/6)
Java多執行緒1:三種實現方法。
分別是繼承Thread類、實現Runnable介面、實現Callable介面通過FutureTask包裝器來建立Thread執行緒。使用ExecutorService、Callable、Future實現有返回結果的多執行緒用到了執行緒池,後面再說。。
其中前兩種方式執行緒執行完後都沒有返回值,後兩種是帶返回值的。
原文連結 :https://www.cnblogs.com/felixzh/p/6036074.html
https://www.cnblogs.com/zivwong/p/9559931.html
1、繼承Thread類建立執行緒
Thread類本質上是實現了Runnable介面的一個例項,代表一個執行緒的例項。啟動執行緒的唯一方法就是通過Thread類的start()例項方法。start()方法是一個native方法,它將啟動一個新執行緒,並執行run()方法。這種方式實現多執行緒很簡單,通過自己的類直接extend Thread,並複寫run()方法,就可以啟動新執行緒並執行自己定義的run()方法。例如:
定義新的類,繼承Thread,重寫run方法:
1 public class MyThread extends Thread { 2 @Override 3 public void run(){ 4 super.run(); 5 System.out.println("執行子執行緒..."); 6 } 7 }View Code
測試用例:
1 public class Test { 2 public static void main(String[] args) { 3 MyThread myThread = newView CodeMyThread(); 4 myThread.start(); 5 System.out.println("主執行緒..."); 6 } 7 }
執行結果:
當然,這裡的結果不代表執行緒的執行順序,執行緒是併發執行的,如果多執行幾次,列印順序可能會不一樣。多執行緒的執行過程中,CPU是以不確定的方式去執行執行緒的,故執行結果與程式碼的執行順序或者呼叫順序無關,執行結果也可能不一樣。
這裡還有一個需要注意的點就是main方法中應該呼叫的是myThread的start方法,而不是run()方法。呼叫start()方法是告訴CPU此執行緒已經準備就緒可以執行,進而系統有時間就會來執行其run()方法。而直接呼叫run()方法,則不是非同步執行,而是等同於呼叫函式般按順序同步執行,這就失去了多執行緒的意義了。
2、實現Runnable介面建立執行緒
如果自己的類已經extends另一個類,就無法直接extends Thread,此時,可以實現一個Runnable介面,如下:
1 public class MyThread extends OtherClass implements Runnable { 2 public void run() { 3 System.out.println("MyThread.run()"); 4 } 5 }View Code
舉例說明:
1 public class MyRunnable implements Runnable { 2 @Override 3 public void run() { 4 System.out.println("執行子執行緒..."); 5 } 6 }View Code
測試用例:
1 public class Test { 2 public static void main(String[] args) { 3 4 Runnable runnable = new MyRunnable(); 5 Thread thread = new Thread(runnable); 6 thread.start(); 7 System.out.println("主執行緒執行結束!"); 8 } 9 }View Code
執行結果:
這裡main中可以看到真正建立新執行緒還是通過Thread建立:
Thread thread = new Thread(runnable);
這一步Thread類的作用就是把run()方法包裝成執行緒執行體,然後依然通過start去告訴系統這個執行緒已經準備好了可以安排執行。
3、使用Callable和Future建立執行緒
上面的兩種方式都有這兩個問題:
- 無法獲取子執行緒的返回值
- run方法不可以丟擲異常
為了解決這兩個問題,我們就需要用到Callable這個介面了。說到介面,上面的Runnable介面實現類例項是作為Thread類的建構函式的引數傳入的,之後通過Thread的start執行run方法中的內容。但是Callable並不是Runnable的子介面,是個全新的介面,它的例項不能直接傳入給Thread構造,所以需要另一個介面來轉換一下。
Java5提供了Future介面來代表Callable接口裡call()方法的返回值,併為Future介面提供了一個實現類FutureTask,該實現類的繼承關係如圖所示:
可以看到,該實現類不僅實現了Future介面,還實現了Runnable介面,所以可以直接傳給Thread建構函式。
而關於FutureTask的建構函式如下:
所以這裡面其實就是要比上一個方法再多一個轉換過程,最終一樣是通過Thread的start來建立新執行緒。有了這個思路,程式碼就很容易理解了:
import java.util.concurrent.Callable; public class MyCallable implements Callable { int i = 0; @Override public Object call() throws Exception { System.out.println(Thread.currentThread().getName()+" i的值:"+ i); return i++; //call方法可以有返回值 } }
測試類:
import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class Test { public static void main(String[] args) { Callable callable = new MyCallable(); for (int i = 0; i < 10; i++) { FutureTask task = new FutureTask(callable); new Thread(task,"子執行緒"+ i).start(); try { //獲取子執行緒的返回值 System.out.println("子執行緒返回值:"+task.get() + "\n"); } catch (Exception e) { e.printStackTrace(); } } } }
部分執行結果: