CountDownLatch是Java並發包中常用的工具類之一,用於實現線程同步,控制線程的執行順序和協調多個線程之間的操作。CountDownLatch的使用可以在多種情景下發揮作用,如:如果一個主線程(或者一個線程)要等待多個子線程完成並且匯總所有結果之後再繼續執行,那麼可以使用CountDownLatch來實現。
一、CountDownLatch簡介
CountDownLatch是Java並發包中提供的一個同步工具類,被廣泛用於多線程並發編程中。它的主要作用是允許一個線程等待一組線程執行完畢後再繼續執行。CountDownLatch內部維護了一個計數器,計數器的初始值為一個正整數,當一個線程執行完畢時,計數器的值減1。當計數器的值降為0時,主線程(或者一組線程)被喚醒,繼續執行後面的流程。
CountDownLatch主要有兩個方法:countDown()和await()。countDown()方法用於將CountDownLatch的計數器減1,await()方法用於阻塞主線程或者一組線程,直到計數器的值降為0。
下面是一個使用CountDownLatch計算1~10的和的例子:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
int n = 10;
CountDownLatch latch = new CountDownLatch(n);
int sum = 0;
for (int i = 1; i {
System.out.println(Thread.currentThread().getName() + " is running...");
sum += num;
latch.countDown();
}, "Thread-" + i).start();
}
latch.await();
System.out.println("Sum of 1~10 is " + sum);
}
}
執行結果如下:
Thread-1 is running... Thread-2 is running... Thread-3 is running... Thread-4 is running... Thread-5 is running... Thread-6 is running... Thread-7 is running... Thread-8 is running... Thread-9 is running... Thread-10 is running... Sum of 1~10 is 55
二、CountDownLatch不生效的情況
在使用CountDownLatch的過程中,有時候會遇到計數器值無法降為0的情況,這裡介紹一些CountDownLatch不生效的情況:
1. 計數器的值被錯誤設置為了0,導致等待線程無法正確計數。
2. 計數器的值被線程錯誤的重置了,導致等待線程的等待時間大於CountDownLatch計數器的減少時間。
3. 使用CountDownLatch的線程太少,無法減少計數器的值,導致等待線程一直在等待。
4. 在計數器的值被減少為0之前,等待線程線程被意外喚醒,導致程序提前退出。
三、Countdown死亡倒計時下載
Countdown死亡倒計時下載是指當下載一個文件時,如果網絡異常或者其他原因導致下載停止,那麼可以使用CountDownLatch來實現下載死亡倒計時,如果下載的時間超過了設置的時間,則自動中斷下載。
下面是一個使用CountDownLatch實現下載死亡倒計時的例子:
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.net.URL;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class DownloadDemo {
public static void main(String[] args) {
String url = "http://example.com/example.txt";
String file = "example.txt";
int timeout = 5000;
download(url, file, timeout);
}
public static void download(String url, String file, int timeout) {
CountDownLatch latch = new CountDownLatch(1);
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.execute(() -> {
try (BufferedInputStream bis = new BufferedInputStream(new URL(url).openConnection().getInputStream());
FileOutputStream fos = new FileOutputStream(file)) {
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
latch.countDown();
}
});
try {
boolean isSuccess = latch.await(timeout, TimeUnit.MILLISECONDS);
if (!isSuccess) {
System.out.println("Download timeout, cancel download.");
executorService.shutdownNow();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
}
}
在以上代碼中,我們使用一個線程池來執行文件下載任務,下載完成後,通過latch.countDown()來表示下載任務已完成。然後在主線程中通過latch.await(timeout, TimeUnit.MILLISECONDS)來等待下載任務完成。如果等待時間超過了設置的超時時間timeout,則自動中斷下載任務,釋放資源。
原創文章,作者:PCYK,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/136132.html
微信掃一掃
支付寶掃一掃