具體什么是多線程和異步執行,這里就不介紹了,能夠點開本篇博客的各位看官們,都是想直奔主題看干貨,不想浪費時間和精力在一些無關緊要的前奏預熱和鋪墊上。本篇博客主要目的是總結,為了在工作中需要用到的時候,隨時可以快速找到,畢竟人的記憶力是有限的。下面我們就快速總結一下 Java 實現多線程異步執行耗時代碼的三種方式,以便在工作中需要用到的時候,隨時可以快速找到。
實現方式一、繼承 Thread 類
繼續 Thread 的子類,需要用到的方法介紹:
方法名 | 說明 |
---|---|
void run() | 在線程開啟后,此方法將被調用執行,不能直接調用該方法實現多線程 |
void start() | 使此方法開啟一個新線程並開始執行,Java虛擬機會自動調用 run方法 |
實現步驟:
- 定義一個類MyThread繼承Thread類
- 在MyThread類中重寫run()方法
- 創建MyThread類的對象
- 啟動線程
代碼實現:
public class MyThread extends Thread {
@Override
public void run() {
for(int i=0; i<50; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
public class MyThreadDemo {
public static void main(String[] args) {
MyThread my1 = new MyThread();
MyThread my2 = new MyThread();
//這里直接調用 run 方法,並不會開啟新線程執行
//my1.run();
//my2.run();
//必須調用 start 方法,才能開啟新線程並自動調用 run 方法
my1.start();
my2.start();
}
}
實現方式二、實現 Runnable 接口
需要用到的 Thread 構造方法介紹:
方法名 | 說明 |
---|---|
Thread(Runnable target) | 傳入實現了 Runnable 接口的類,構造一個 Thread 對象 |
Thread(Runnable target, String name) | 傳入實現了 Runnable 接口的類,構造一個名稱為 name 的 Thread 對象 |
實現步驟:
- 定義一個類 MyRunnable 實現 Runnable 接口
- 在 MyRunnable 類中實現 run() 方法
- 創建 MyRunnable 類的對象
- 創建 Thread 類的對象,把 MyRunnable 對象作為構造方法的參數
- 啟動線程
代碼實現:
public class MyRunnable implements Runnable {
@Override
public void run() {
for(int i=0; i<50; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
public class MyRunnableDemo {
public static void main(String[] args) {
MyRunnable my = new MyRunnable();
//Thread t1 = new Thread(my);
//Thread t2 = new Thread(my);
Thread t1 = new Thread(my,"線程一");
Thread t2 = new Thread(my,"線程二");
//啟動線程
t1.start();
t2.start();
}
}
實現方式三、 實現 Callable 接口
相關方法介紹:
方法名 | 說明 |
---|---|
V call() | 這是 Callable 接口中要實現的方法,相當於 Runnable 接口中的 run 方法 |
FutureTask(Callable<V> callable) | 使用 Callable 接口實現類實例創建一個 FutureTask,它運行時會調配用 Callable 接口中的 call 方法 |
V get() | FutureTask 實例的 get 方法,可以阻塞代碼繼續往下執行,直到獲取到異步線程中的返回結果為止 |
實現步驟:
- 定義一個類 MyCallable 實現 Callable 接口
- 在 MyCallable 類中重實現 call() 方法
- 創建 MyCallable 類的對象
- 創建 FutureTask 對象,把 MyCallable 對象作為構造方法的參數
- 創建 Thread 類的對象,把 FutureTask 對象作為構造方法的參數
- 啟動線程
- 如果想獲取返回值的話,可以調用get方法,就可以獲取線程結束之后的結果
代碼實現:
//因為這里想返回 String 值,所以實現 String 類型的 Callable 接口
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
//這里返回一個字符串
return "這是我返回的字符串結果";
}
}
public class CallableDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable mc = new MyCallable();
//因為 MyCallable 實現了 String 類型的 Callable 接口
//所以返回值也是 String 類型,所以創建的是 String 類型的 FutureTask 對象
FutureTask<String> ft = new FutureTask<>(mc);
//傳入 FutureTask 實例,創建線程對象
Thread t1 = new Thread(ft);
//不能在這個地方使用 FutureTask 的 get 方法獲取異步線程的返回值,否則程序將卡死在這里。
//因為 t1 線程還沒有執行,所以無法獲取到返回值,所以如果執行 get 方法,程序將卡死在這里。
//String s = ft.get();
//開啟新線程,異步執行 MyCallable 實例中的 call 方法邏輯
t1.start();
//這里編寫一些實現其它業務邏輯代碼進行執行
//可以做一些其它比較耗時的任務
//......
//獲取異步線程的返回值
String s = ft.get();
System.out.println(s);
}
}
三種實現方式的總結
1 繼承 Thread 類這種實現方式,實現比較簡單,但是擴展性差,因為類只能單繼承。
2 實現 Runnable 接口、Callable 接口這兩種實現方式,實現比較復雜,但是擴展性比較強。
3 如果想要獲取到異步線程中的返回值的話,可以采用實現 Callable 接口這種實現方式。