Java 實現多線程【異步】的三種方式


具體什么是多線程和異步執行,這里就不介紹了,能夠點開本篇博客的各位看官們,都是想直奔主題看干貨,不想浪費時間和精力在一些無關緊要的前奏預熱和鋪墊上。本篇博客主要目的是總結,為了在工作中需要用到的時候,隨時可以快速找到,畢竟人的記憶力是有限的。下面我們就快速總結一下 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 接口這種實現方式。




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM