在 Java 中,執行緒的並行等待(即等待多個執行緒並行執行完畢)通常可以透過以下幾種方式來實現。我們要確保多個執行緒能夠並行執行,並在所有執行緒執行完畢後再繼續後續的操作。這種場景通常可以用在併發任務的協調中。以下是幾種常見的實現方式:
1. 使用 Thread.join()
方法
join()
方法是最直接的方式,用於等待一個執行緒執行完畢。為了實現並行等待多個執行緒的完成,可以對每個執行緒分別呼叫 join()
,主執行緒會依次等待每個子執行緒執行結束。
示例:
public class ParallelWaitExample {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
System.out.println("Thread 1 started");
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("Thread 1 finished");
});
Thread thread2 = new Thread(() -> {
System.out.println("Thread 2 started");
try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("Thread 2 finished");
});
thread1.start();
thread2.start();
// 主執行緒等待兩個執行緒完成
thread1.join(); // 等待 thread1 完成
thread2.join(); // 等待 thread2 完成
System.out.println("All threads finished");
}
}
解釋:在這個例子中,主執行緒依次呼叫 thread1.join()
和 thread2.join()
,確保在兩個執行緒都執行完畢後繼續執行後續邏輯。
2. 使用 CountDownLatch
CountDownLatch
是一個同步工具類,可以用來協調多個執行緒的執行順序。透過它,主執行緒可以等待一組執行緒執行完畢。
使用步驟:
- 建立
CountDownLatch
,初始值為執行緒數。 - 每個執行緒在任務完成後呼叫
countDown()
,表示自己完成任務。 - 主執行緒呼叫
await()
方法,等待計數器歸零(即所有執行緒完成任務)。
示例:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
int numThreads = 2;
CountDownLatch latch = new CountDownLatch(numThreads);
Thread thread1 = new Thread(() -> {
System.out.println("Thread 1 started");
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("Thread 1 finished");
latch.countDown(); // 任務完成,計數器減1
});
Thread thread2 = new Thread(() -> {
System.out.println("Thread 2 started");
try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("Thread 2 finished");
latch.countDown(); // 任務完成,計數器減1
});
thread1.start();
thread2.start();
latch.await(); // 主執行緒等待所有執行緒完成
System.out.println("All threads finished");
}
}
解釋:CountDownLatch
的計數器初始值為 2,表示等待兩個執行緒完成。每個執行緒執行完畢後呼叫 countDown()
,主執行緒呼叫 await()
阻塞,直到所有執行緒完成任務。
3. 使用 CyclicBarrier
CyclicBarrier
是一個同步輔助工具,用來讓一組執行緒等待彼此到達一個共同的屏障點。當指定數量的執行緒都呼叫了 await()
方法時,所有執行緒才會繼續執行。
示例:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
public static void main(String[] args) {
int numThreads = 2;
CyclicBarrier barrier = new CyclicBarrier(numThreads, () -> {
// 所有執行緒都到達屏障後執行該任務
System.out.println("All threads finished");
});
Thread thread1 = new Thread(() -> {
System.out.println("Thread 1 started");
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("Thread 1 finished");
try { barrier.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); }
});
Thread thread2 = new Thread(() -> {
System.out.println("Thread 2 started");
try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("Thread 2 finished");
try { barrier.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); }
});
thread1.start();
thread2.start();
}
}
解釋:CyclicBarrier
的構造引數是 2,表示兩個執行緒都需要到達屏障(呼叫 await()
)後才能繼續執行。所有執行緒到達後,屏障會觸發並執行定義的任務。
4. 使用 ExecutorService
和 Future
ExecutorService
提供了一種管理執行緒池的方式,它可以提交一組任務並使用 Future
來等待它們的完成。
示例:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.Callable;
import java.util.ArrayList;
import java.util.List;
public class ExecutorServiceExample {
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(2);
List<Callable<String>> tasks = new ArrayList<>();
tasks.add(() -> {
System.out.println("Task 1 started");
Thread.sleep(1000);
System.out.println("Task 1 finished");
return "Result 1";
});
tasks.add(() -> {
System.out.println("Task 2 started");
Thread.sleep(2000);
System.out.println("Task 2 finished");
return "Result 2";
});
// 提交任務並獲取 Future 列表
List<Future<String>> results = executor.invokeAll(tasks);
for (Future<String> result : results) {
System.out.println("Task result: " + result.get()); // 等待任務完成並獲取結果
}
executor.shutdown();
}
}
解釋:使用 ExecutorService
建立一個執行緒池,透過 invokeAll()
提交任務,主執行緒會阻塞直到所有任務執行完畢,然後可以透過 Future.get()
獲取每個任務的結果。
總結
Thread.join()
:簡單、直接地等待每個執行緒完成,但需要逐個呼叫。CountDownLatch
:適合在多個執行緒完成任務後觸發主執行緒的後續操作。CyclicBarrier
:適合需要多執行緒相互等待的場景。ExecutorService
和Future
:執行緒池管理,並行執行任務並等待所有任務完成,適用於多工並行執行場景。
你可以根據應用的具體需求選擇合適的並行等待機制。