Java多執行緒的三種實現方式
今天簡單說一下Java三種多執行緒實現方式和區別,主要有實現Runnable、Callable和繼承Thread三種方式。
實現Runnable的方式
這種方式比較常用,當我們的執行緒類有繼承其他的類的情況下(Java不支援類多繼承),並且執行緒任務不需要返回值的情況下可以選用這種方式。
1 public class ThreadRunnableDemo implements Runnable{ 2 3 /** 計數變數 */ 4 private int count = 0; 5 6 public static void main(String[] args) throws InterruptedException { 7 8 ThreadRunnableDemo threadRunnableDemo = new ThreadRunnableDemo(); 9 10 //例項化執行緒 11 Thread thread = new Thread(threadRunnableDemo, "threadRunnableDemoA"); 12 System.out.println(String.format("執行緒狀態preStart: %s", thread.getState())); 13 14 //啟動執行緒 15 thread.start(); 16 System.out.println(String.format("執行緒狀態afterStart: %s", thread.getState())); 17 18 //主執行緒休眠1000ms 19 Thread.sleep(1000); 20 System.out.println(String.format("執行緒狀態after1000ms: %s", thread.getState())); 21 22 } 23 24 @Override 25 public void run() { 26 27 count++; 28 29 System.out.println(String.format("執行緒名稱:%s, 執行緒狀態:%s, count:%s", 30 Thread.currentThread().getName(), Thread.currentThread().getState(), count)); 31 32 } 33 }
輸出結果:
1 執行緒狀態preStart: NEW 2 執行緒狀態afterStart: RUNNABLE 3 執行緒名稱:threadRunnableDemoA, 執行緒狀態:RUNNABLE, count:1 4 執行緒狀態after1000ms: TERMINATED
實現Callable的方式
當我們執行執行緒需要返回值的時候那麼就必須選用實現Callable類的方式,因為目前只有這種方式能返回值。當然這種方式我們也可以不需要獲取返回值。
這種方式是通過FutureTask的get()方法(下面程式碼的第22行)或者get(long timeout, TimeUnit unit)(下面程式碼的第28行)方法獲取返回值。當我們看Callable的介面定義的原始碼會發現“public interface Callable<V> ” ,我們實現的時候是需要定義返回型別,如下面程式碼所示。
除此之外我們還需要注意的是:當我們通過FutureTask的get()方法去獲取執行緒的返回值的時候是要等到執行緒call()內容都執行完畢之後才能獲取得到,並且get()方法後面的程式碼必須等待,說明這一定是同步的,所以我們可以在真正需要執行緒返回值的時候才通過get()方法去獲取,以免被阻塞。當我們通過get(long timeout, TimeUnit unit)方式去獲取的時候可以設定超時時間,如果超過所設定的超時時間都沒有獲取到執行緒返回的值則會丟擲 java.util.concurrent.TimeoutException 異常,當然如果在get(long timeout, TimeUnit unit)之前用get()方式獲取了的話就不會拋異常。
實現Callable還有個好處就是可以執行緒可以拋異常,如果我們需要線上程裡丟擲異常的話也可以選用這種方式,其他兩種方式只能捕獲異常資訊。
1 public class ThreadCallableDemo implements Callable<Integer>{ 2 3 /** 計數變數 */ 4 private int count = 0; 5 6 public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException { 7 8 ThreadCallableDemo threadCallableDemo = new ThreadCallableDemo(); 9 10 //通過FutureTask獲取返回值 11 FutureTask<Integer> taskA = new FutureTask<>(threadCallableDemo); 12 13 //例項化執行緒 14 Thread thread = new Thread(taskA, "threadCallableDemoA"); 15 System.out.println(String.format("執行緒狀態preStart: %s", thread.getState())); 16 17 //啟動執行緒 18 thread.start(); 19 System.out.println(String.format("執行緒狀態afterStart: %s", thread.getState())); 20 21 //通過FutureTask的get()方法獲取返回值 22 int result = taskA.get(); 23 System.out.println("是否同步測試...."); 24 System.out.println(String.format("result: %s", result)); 25 System.out.println(String.format("執行緒狀態afterGetResult1: %s", thread.getState())); 26 27 //通過FutureTask的get()方法獲取返回值 設定超時時間 單位為ms 28 int resultWithTime = taskA.get(100, TimeUnit.MILLISECONDS); 29 System.out.println(String.format("resultWithTime: %s", resultWithTime)); 30 System.out.println(String.format("執行緒狀態afterGetResult2: %s", thread.getState())); 31 32 } 33 34 /** 35 * 實現Callable的call類 36 */ 37 @Override 38 public Integer call() throws Exception { 39 40 //自增 41 count++; 42 43 System.out.println(String.format("執行緒名稱:%s, 執行緒狀態:%s, count:%s", 44 Thread.currentThread().getName(), Thread.currentThread().getState(), count)); 45 System.out.println("休眠1000ms...."); 46 Thread.currentThread().sleep(1000); 47 return count; 48 } 49 }
輸出結果:
1 執行緒狀態preStart: NEW 2 執行緒狀態afterStart: RUNNABLE 3 執行緒名稱:threadCallableDemoA, 執行緒狀態:RUNNABLE, count:1 4 休眠1000ms.... 5 是否同步測試.... 6 result: 1 7 執行緒狀態afterGetResult1: TERMINATED 8 resultWithTime: 1 9 執行緒狀態afterGetResult2: TERMINATED
繼承Thread的方式
Thread類實際上也是實現Runnable介面,所以當我們繼承Thread的時候我們即使不實現run()方法也不會報錯,這種方式也經常用。
下面我寫了兩種不同繼承Thread的程式碼,大家可以看一下區別,我在網上看到很多人說 繼承Thread實現多執行緒,執行緒間不能共享資料,但是我用下面的程式碼1方式似乎也可以共享哇,歡迎大家提出質疑。
程式碼1:
1 public class ThreadThreadDemo extends Thread{ 2 3 /** 計數變數 */ 4 private int count = 0; 5 6 public static void main(String[] args) throws InterruptedException { 7 8 ThreadThreadDemo threadThreadDemo = new ThreadThreadDemo(); 9 10 //例項化執行緒 11 Thread thread = new Thread(threadThreadDemo, "threadThreadDemoA"); 12 System.out.println(String.format("執行緒狀態preStart: %s", thread.getState())); 13 14 //啟動執行緒 15 thread.start(); 16 System.out.println(String.format("執行緒狀態afterStart: %s", thread.getState())); 17 18 //主執行緒休眠1000s 19 Thread.sleep(1000); 20 System.out.println(String.format("執行緒狀態after1000ms: %s", thread.getState())); 21 } 22 23 @Override 24 public void run() { 25 26 count++; 27 28 System.out.println(String.format("執行緒名稱:%s, 執行緒狀態:%s, count:%s", 29 Thread.currentThread().getName(), Thread.currentThread().getState(), count)); 30 } 31 }
輸出結果1:
1 執行緒狀態preStart: NEW 2 執行緒狀態afterStart: RUNNABLE 3 執行緒名稱:threadThreadDemoA, 執行緒狀態:RUNNABLE, count:1 4 執行緒狀態after1000ms: TERMINATED
程式碼2:
1 public class ThreadThreadDemo extends Thread{ 2 3 /** 計數變數 */ 4 private int count = 0; 5 6 public static void main(String[] args) throws InterruptedException { 7 8 ThreadThreadDemo threadThreadDemo = new ThreadThreadDemo(); 9 10 //例項化執行緒 11 System.out.println(String.format("執行緒狀態preStart: %s", threadThreadDemo.getState())); 12 13 //啟動執行緒 14 threadThreadDemo.start(); 15 System.out.println(String.format("執行緒狀態afterStart: %s", threadThreadDemo.getState())); 16 17 //主執行緒休眠1000s 18 Thread.sleep(1000); 19 System.out.println(String.format("執行緒狀態after1000ms: %s", threadThreadDemo.getState())); 20 } 21 22 @Override 23 public void run() { 24 25 count++; 26 27 System.out.println(String.format("執行緒名稱:%s, 執行緒狀態:%s, count:%s", 28 Thread.currentThread().getName(), Thread.currentThread().getState(), count)); 29 } 30 }
輸出結果2:
1 執行緒狀態preStart: NEW 2 執行緒狀態afterStart: RUNNABLE 3 執行緒名稱:Thread-0, 執行緒狀態:RUNNABLE, count:1 4 執行緒狀態after1000ms: TERMINATED
最後總結:
- 如果不要求執行緒返回結果,也不需要拋異常也沒有繼承其他的類,那麼三種方式可以任選,看喜好;
- 如果有繼承其他類,那麼就只能用實現Runnable和實現Callable的方式;
- 如果需要執行緒返回結果或者需要執行緒拋異常那麼選擇實現Callable的方式的方式,但是需要注意的是獲取返回結果是同步的方式。
如果有疑問或者有問題歡迎留言討論!
&n