Java 執行緒建立的幾種方式

語言: CN / TW / HK

「這是我參與2022首次更文挑戰的第2天,活動詳情檢視:2022首次更文挑戰」。

我們在工作中經常會用到多執行緒開發,今天我們一起來梳理一下執行緒建立的幾種方式: 1. 繼承 Thread 執行緒類 2. 實現 Runnable 介面 3. 建立 Callable 介面的實現類 4. 使用拉姆達表示式建立(1.8) 5. ForkJoinPool 執行緒池 6. 繼承 RecursiveAction, 無返回值 7. 繼承 RecursiveTask, 帶返回值的 8. 使用執行緒池 ThreadPoolExecutor 9. 使用併發包 Executors 工具類建立

繼承 Thread 類

繼承執行緒類,然後重寫 run 方法,呼叫執行緒 start 方法後, 等待 JVM 為當前執行緒分配到 CPU 時間片會呼叫 run 方法執行。

java class Thread1 extends Thread { @Override public void run() { //dosomething() System.out.println("Thread1"); } }

實現 Runnable 介面

實現 Runnable 介面,重寫 run 方法,執行執行緒需要丟入 Runnable 介面實現類,呼叫 Thread 物件的 start 方法執行。 ```java // 定義執行緒 class Runner1 implements Runnable { @Override
public void run() { //dosomething() System.out.println("Runner1"); } }

// 執行緒執行 new Thread thread = new Thread(new Runner1()); thrad.start(); ```

建立 Callable 介面的實現類

Callable: 返回結果並且可能丟擲異常的任務。 優點:

  • 可以獲得任務執行返回值;
  • 通過與 Future 的結合,可以實現利用 Future 來跟蹤非同步計算的結果。

```java class Callable1 implements Callable {
@Override public String call() throws Exception { return "1"; } }

FutureTask stringFutureTask = new FutureTask<>(new Callable1()); stringFutureTask.run(); ```

使用流建立

其實本質是通過 Lambda(拉姆達)表示建立執行緒,也就是得到一個 Runnable 介面的例項。 java Runnable runnable = () -> { System.out.println("createThreadForStream"); }; new Thread(runnable).start();

ForkJoinPool 執行緒池

ForkJoinPool:Java提供了ForkJoinPool來支援將一個任務拆分成多個“小任務”平行計算,再把多個“小任務”的結果合成總的計算結果。ForkJoinPool是ExecutorService的實現類,因此是一種特殊的執行緒池。ForkJoinPool提供瞭如下兩個常用的構造器。

public ForkJoinPool(int parallelism):建立一個包含parallelism個並行執行緒的ForkJoinPool。 public ForkJoinPool():以 Runtime.getRuntime().availableProcessors()的返回值作為 parallelism 來建立 ForkJoinPool。 ​

建立ForkJoinPool例項後,可以呼叫 ForkJoinPool 的 submit(ForkJoinTask task) 或者invoke(ForkJoinTask task) 來執行指定任務。其中 ForkJoinTask 代表一個可以並行、合併的任務。ForkJoinTask 是一個抽象類,它有兩個抽象子類:RecursiveAction 和 RecursiveTask。

  • RecursiveTask:代表有返回值的任務;
  • RecursiveAction:代表沒有返回值的任務;

我們就可以通過 RecursiveTask、 RecursiveAction 例項來建立執行緒。 java new ForkJoinPool().submit(new Thread1());

繼承 RecursiveAction 無返回值

繼承 RecursiveAction 我們可以建立不帶返回值的執行緒物件。

```java class PrintTask extends RecursiveAction {
@Override protected void compute() { System.out.println("RecursiveAction 子類"); } }

new ForkJoinPool().submit(new PrintTask()); ```

繼承 RecursiveTask 獲取返回值

繼承 RecursiveTask我們可以建立帶返回值的執行緒。 ```java class CalcuteTask extends RecursiveTask { @Override protected Integer compute() { return 10; }

}

Executors.newSingleThreadExecutor().submit(new CalcuteTask()); ForkJoinTask submit = new ForkJoinPool().submit(new CalcuteTask()); Integer res = submit.get(); ```

使用執行緒池

ThreadPoolExecutor是我們建立執行緒池的核心工具類,我們建立執行緒池後。execute或者 submit方法提交執行緒執行任務,進入執行緒池中。等待執行緒池為我們分配資源執行。並且 ThreadPoolExecutor 提供了非常豐富的執行緒池配置引數。

通過 ThreadPoolExecutor 建立執行緒池,也是 Alibaba java 程式設計規範中倡導的變成方式。 java ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 2, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10)); threadPoolExecutor.execute(new Thread1());

使用併發包 Executors

Executors其實就是對 ThreadPoolExecutor的封裝,是一個典型的工廠方法模式。提供了非常多的建立執行緒池的方法。比如:
- public static ExecutorService newFiexedThreadPool(int Threads) 建立固定數目執行緒的執行緒池。 - public static ExecutorService newCachedThreadPool():建立一個可快取的執行緒池,呼叫execute 將重用以前構造的執行緒(如果執行緒可用)。如果沒有可用的執行緒,則建立一個新執行緒並新增到池中。終止並從快取中移除那些已有 60 秒鐘未被使用的執行緒。 - public static ExecutorService newSingleThreadExecutor():建立一個單執行緒的Executor。 - public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 建立一個支援定時及週期性的任務執行的執行緒池,多數情況下可用來替代Timer類。

java Executors.newSingleThreadExecutor().execute(new Thread1()); Executors.newFixedThreadPool(8).execute(new Thread1()); Executors.newWorkStealingPool(8).execute(new Thread1()); Executors.newCachedThreadPool().execute(new Thread1()); Executors.newSingleThreadScheduledExecutor().schedule(new Thread1(), 2, TimeUnit.SECONDS); Executors.newScheduledThreadPool(8).schedule(new Thread1(), 2, TimeUnit.SECONDS);

總結

Java 建立執行緒任務過後,JVM 底層會建立一個 JavaThread 和一個 OSThread, 將 Java 執行緒物件和作業系統核心態的程序繫結。