1. 程式人生 > >Java多執行緒的三種實現方式

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

 

最後總結:

  1. 如果不要求執行緒返回結果,也不需要拋異常也沒有繼承其他的類,那麼三種方式可以任選,看喜好;
  2. 如果有繼承其他類,那麼就只能用實現Runnable和實現Callable的方式;
  3. 如果需要執行緒返回結果或者需要執行緒拋異常那麼選擇實現Callable的方式的方式,但是需要注意的是獲取返回結果是同步的方式。

如果有疑問或者有問題歡迎留言討論!

 

&n