在現代的多線程編程中,Semaphore和CountDownLatch是兩個非常常見和重要的工具類,它們都可以用來實現多線程間的同步和互斥,提高程序的併發性能和效率。本文將詳細介紹Java中的Semaphore和CountDownLatch這兩個工具類的使用方法和實際應用場景。
一、Semaphore
1.1 概述
Semaphore是Java中的一個同步工具類,用來控制多個線程對共享資源的訪問。它主要用於控制併發線程的數量,可以設置一定數量的許可證,每當一個線程訪問共享資源時,會消耗一個許可證,當許可證用盡時,其他線程就會被阻塞,直到有線程釋放許可證爲止。
1.2 使用
Semaphore的常用方法如下:
- acquire(int permits): 獲取指定數量的許可證,如果沒有足夠的許可證,當前線程將會被阻塞。
- tryAcquire(int permits): 嘗試獲取指定數量的許可證,如果可以獲取則返回true,否則返回false。
- release(int permits): 釋放指定數量的許可證,喚醒被阻塞的線程。
使用Semaphore的典型場景是控制線程的併發數量,下面我們來看一個簡單的例子:
import java.util.concurrent.Semaphore;
public class SemaphoreTest {
public static void main(String[] args) {
int N = 8; // 任務數
Semaphore semaphore = new Semaphore(5); // 最大併發數爲5
for (int i = 0; i < N; i++) {
new Worker(i, semaphore).start(); // 啓動多個線程
}
}
static class Worker extends Thread {
private int num;
private Semaphore semaphore;
public Worker(int num, Semaphore semaphore) {
this.num = num;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire(); // 獲取許可證
System.out.println("Worker " + num + " is working");
Thread.sleep(2000);
System.out.println("Worker " + num + " has finished");
semaphore.release(); // 釋放許可證
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
上面的代碼中,我們創建了一個Semaphore對象,並設置最大併發數爲5。然後創建了8個線程,每當一個線程開始工作時,需要先獲取一個許可證,如果沒有足夠的許可證,則會被阻塞,直到其他線程釋放許可證爲止。這樣就可以有效控制線程的併發數量,避免系統資源過度消耗。
二、CountDownLatch
2.1 概述
CountDownLatch也是Java中的一個同步工具類,它用於控制一個或多個線程等待其他線程完成任務後再執行。CountDownLatch的工作方式比較簡單,它會在初始化時設置一個計數器,每當一個任務完成時,就將計數器減一;當計數器爲0時,代表所有任務都已經完成,等待的線程可以開始執行了。
2.2 使用
CountDownLatch的常用方法如下:
- CountDownLatch(int count):初始化一個CountDownLatch對象,並設置計數器初始值。
- void await():調用此方法會使當前線程等待,直到計數器爲0才繼續執行。
- void countDown():將計數器減1,當計數器爲0時,等待的線程會被喚醒。
下面我們來看一個實際應用場景,假設要求在多個線程中進行文件拆分和合並操作,需要先將文件拆分成若干個塊,然後多個線程並行處理這些塊,最後再將處理結果合併成一個完整的文件。這個需求可以使用CountDownLatch來實現。
import java.util.concurrent.CountDownLatch;
public class CountDownLatchTest {
private static final int THREAD_COUNT = 4; // 線程數
private static final CountDownLatch startLatch = new CountDownLatch(1); // 開始信號
private static final CountDownLatch endLatch = new CountDownLatch(THREAD_COUNT); // 結束信號
public static void main(String[] args) {
for (int i = 0; i < THREAD_COUNT; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " is ready");
startLatch.await(); // 等待開始信號
System.out.println(Thread.currentThread().getName() + " is working");
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " has finished");
endLatch.countDown(); // 發送結束信號
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
System.out.println("All threads are ready, start working...");
startLatch.countDown(); // 發送開始信號
try {
endLatch.await(); // 等待所有線程結束
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All threads have finished their work");
}
}
上面的代碼中,我們創建了4個線程,並使用CountDownLatch來控制線程的同步和互斥。首先創建了兩個CountDownLatch對象,一個用於發出開始信號,一個用於接收結束信號。然後啓動了4個線程,每當一個線程開始工作時,需要等待開始信號發出,如果沒有收到開始信號,則會一直等待;當所有線程都已經完成任務後,需要發送結束信號,以便主線程可以繼續執行。
三、總結
通過以上示例,我們可以看到在實際應用中,Semaphore和CountDownLatch也都是非常實用的工具類,它們可以幫助程序員有效控制多線程的併發數量和任務執行順序,提高程序的性能和效率。有了這兩個工具類的幫助,我們可以更加方便地進行多線程編程,實現更加複雜的業務邏輯。需要注意的是,在使用這兩個工具類時,應該結合實際需求場景來選擇合適的方法和參數,避免程序出現不必要的死鎖和阻塞。