深入理解 Java 多執行緒、Lambda 表示式及執行緒安全最佳實踐

小万哥丶發表於2024-03-15

Java 執行緒

執行緒使程式能夠透過同時執行多個任務而更有效地執行。

執行緒可用於在不中斷主程式的情況下在後臺執行復雜的任務。

建立執行緒

有兩種建立執行緒的方式。

  1. 擴充套件Thread類

可以透過擴充套件Thread類並覆蓋其run()方法來建立執行緒:

public class MyThread extends Thread {
  public void run() {
    System.out.println("This code is running in a thread");
  }
}
  1. 實現Runnable介面

另一種建立執行緒的方式是實現Runnable介面:

public class MyRunnable implements Runnable {
  public void run() {
    System.out.println("This code is running in a thread");
  }
}

執行執行緒

  1. 擴充套件Thread類

如果類擴充套件Thread類,則可以透過建立類的例項並呼叫其start()方法來執行執行緒:

public class Main {
  public static void main(String[] args) {
    MyThread myThread = new MyThread();
    myThread.start();
    System.out.println("This code is outside of the thread");
  }
}
  1. 實現Runnable介面

如果類實現了Runnable介面,則可以透過將類的例項傳遞給Thread物件的建構函式,然後呼叫執行緒的start()方法來執行執行緒:

public class Main {
  public static void main(String[] args) {
    MyRunnable myRunnable = new MyRunnable();
    Thread thread = new Thread(myRunnable);
    thread.start();
    System.out.println("This code is outside of the thread");
  }
}

區分“擴充套件”和“實現”執行緒

主要區別在於,當一個類擴充套件Thread類時,無法擴充套件任何其他類,但透過實現Runnable介面,可以擴充套件另一個類,例如:

class MyClass extends OtherClass implements Runnable

併發問題

因為執行緒與程式的其他部分同時執行,所以無法知道程式碼將按照什麼順序執行。當執行緒和主程式同時讀取和寫入相同的變數時,值是不可預測的。由此導致的問題稱為併發問題。

示例

一個變數amount值不可預測的程式碼示例:

public class Main extends Thread {
  public static int amount = 0;

  public static void main(String[] args) {
    Main thread = new Main();
    thread.start();
    System.out.println(amount);
    amount++;
    System.out.println(amount);
  }

  public void run() {
    amount++;
  }
}

為避免併發問題,最好儘可能少地線上程之間共享屬性。如果需要共享屬性,一種可能的解決方案是在使用執行緒可以更改的任何屬性之前,使用執行緒的isAlive()方法檢查執行緒是否已完成執行。

示例

使用isAlive()防止併發問題:

public class Main extends Thread {
  public static int amount = 0;

  public static void main(String[] args) {
    Main thread = new Main();
    thread.start();
    // 等待執行緒完成
    while (thread.isAlive()) {
      System.out.println("Waiting...");
    }
    // 更新amount並列印其值
    System.out.println("Main program: " + amount);
    amount++;
    System.out.println("Main program: " + amount);
  }

  public void run() {
    amount++;
  }
}

執行緒池

執行緒池是一種管理執行緒的資源。它允許您建立並維護一組可重用的執行緒。使用執行緒池可以提高應用程式的效能和效率。

執行緒安全

執行緒安全是指多個執行緒可以訪問和修改資料而不導致資料損壞。使資料執行緒安全的一種方法是使用同步。同步是一種機制,它允許執行緒一次一個地訪問共享資料。

常見的執行緒安全問題

  • 競態條件:當多個執行緒同時訪問共享資料並嘗試對其進行更改時,就會發生競態條件。這可能導致資料損壞。
  • 原子性:原子操作是指不可分割的操作。當多個執行緒嘗試同時執行原子操作時,可能會導致資料損壞。
  • 可見性:當一個執行緒對共享資料進行更改時,其他執行緒必須能夠看到這些更改。

避免執行緒安全問題

  • 使用同步
  • 使用不可變物件
  • 使用原子操作

Java Lambda表示式

Lambda表示式簡介

Lambda表示式是在Java 8中引入的。Lambda表示式是一小段程式碼塊,它接受引數並返回一個值。Lambda表示式類似於方法,但它們不需要名稱,並且可以直接在方法體中實現。

Lambda表示式的語法

最簡單的Lambda表示式包含一個引數和一個表示式:

引數 -> 表示式

要使用多個引數,請將它們放在括號中:

(引數1, 引數2) -> 表示式

表示式是有限制的。它們必須立即返回一個值,並且不能包含變數、賦值或if或for等語句。為了執行更復雜的操作,可以使用帶有花括號的程式碼塊。如果Lambda表示式需要返回一個值,那麼程式碼塊應該有一個return語句。

(引數1, 引數2) -> { 程式碼塊 }

Lambda表示式的使用

Lambda表示式通常作為引數傳遞給函式。在以下示例中,Lambda表示式作為引數傳遞給ArrayList的forEach()方法,以列印列表中的每個項:

import java.util.ArrayList;

public class Main {
  public static void main(String[] args) {
    ArrayList<Integer> numbers = new ArrayList<>();
    numbers.add(5);
    numbers.add(9);
    numbers.add(8

);
    numbers.add(1);
    numbers.forEach((n) -> { System.out.println(n); });
  }
}

Lambda表示式的儲存

如果變數的型別是僅具有一個方法的介面,那麼Lambda表示式可以儲存在變數中。Lambda表示式應該具有與該方法相同數量的引數和相同的返回型別。Java內建了許多這類介面,如Consumer介面(在java.util包中),它被列表使用。

import java.util.ArrayList;
import java.util.function.Consumer;

public class Main {
  public static void main(String[] args) {
    ArrayList<Integer> numbers = new ArrayList<>();
    numbers.add(5);
    numbers.add(9);
    numbers.add(8);
    numbers.add(1);
    Consumer<Integer> method = (n) -> { System.out.println(n); };
    numbers.forEach(method);
  }
}

Lambda表示式作為方法引數

要在方法中使用Lambda表示式,該方法應該有一個以單一方法介面作為其型別的引數。呼叫介面的方法將執行Lambda表示式。

interface StringFunction {
  String run(String str);
}

public class Main {
  public static void main(String[] args) {
    StringFunction exclaim = (s) -> s + "!";
    StringFunction ask = (s) -> s + "?";
    printFormatted("Hello", exclaim);
    printFormatted("Hello", ask);
  }

  public static void printFormatted(String str, StringFunction format) {
    String result = format.run(str);
    System.out.println(result);
  }
}

Lambda表示式的優勢

  • 簡化程式碼
  • 提高可讀性
  • 增強程式碼的表達力

Lambda 表示式是 Java 8 中引入的一項強大功能,可以簡化程式碼並提高可讀性。它們是函數語言程式設計的重要組成部分,可以用於各種任務,例如資料處理、事件處理和流處理。

最後

為了方便其他裝置和平臺的小夥伴觀看往期文章:

微信公眾號搜尋:Let us Coding,關注後即可獲取最新文章推送

看完如果覺得有幫助,歡迎 點贊、收藏、關注

相關文章