在多線程編程的時候,有時候需要控制併發流,Java本身提供了幾個控制併發的工具類,比如CountDownLatch,CyclicBarrier,Semaphore等。
1、CountDownLatch允許一個或者多個線程等等其他線程完成。
如果有個會議,等所有的人到了才能開始,假如每個人都是一個線程,開會需要等待每個線程結束。
CountDownLatch.countDown這個方法是CountDownLatch會把計數器的值減1,CountDownLatch.await等待計數器爲0時纔會執行下面的代碼。
package com.example.demo;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class CountDownLatchTest {
//10個線程的線程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
CountDownLatch countDownLatch = new CountDownLatch(20);
//計數器
volatile AtomicInteger atomicInteger = new AtomicInteger(1);
//等多有人到齊開會
private void meeting() throws InterruptedException{
for(int i=1;i<=20;i++)
{
executorService.execute(()->{
System.out.println("大家好,帥哥"+atomicInteger.getAndIncrement()+"報到!");
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
System.out.println("人已到齊,開始開會!");
}
public static void main(String[] args){
CountDownLatchTest test = new CountDownLatchTest();
try{
test.meeting();
}catch (InterruptedException e){
}
}
}
輸出結果如下,等所有的線程結束,纔會往下執行。
大家好,帥哥1報到!
大家好,帥哥2報到!
大家好,帥哥3報到!
大家好,帥哥4報到!
大家好,帥哥5報到!
大家好,帥哥6報到!
大家好,帥哥7報到!
大家好,帥哥8報到!
大家好,帥哥9報到!
大家好,帥哥10報到!
大家好,帥哥11報到!
大家好,帥哥12報到!
大家好,帥哥14報到!
大家好,帥哥13報到!
大家好,帥哥20報到!
大家好,帥哥19報到!
大家好,帥哥17報到!
大家好,帥哥18報到!
大家好,帥哥15報到!
大家好,帥哥16報到!
人已到齊,開始開會!
2、CountDownLatch 控制異步和同步
應用場景舉例:
執行A項目的方法,需要調用B項目、C項目、D項目的接口方法。
需求:
異步調用B、C、D項目的接口方法,且每個接口都調用結束後,A項目的方法纔可以結束。
注:如果需要獲取接口返回結果,可以使用緩存(key,value)保存。線程只支持線程外的靜態參數傳遞,不嚴謹。
public static void main(String[] args) throws InterruptedException {
System.out.println("<=================A項目方法開始==============>");
CountDownLatch latch = new CountDownLatch(3);
Thread B = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("B項目接口調用===開始");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("B項目接口調用===結束");
latch.countDown();
}
});
Thread C = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("C項目接口調用===開始");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("C項目接口調用===結束");
latch.countDown();
}
});
Thread D = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("D項目接口調用===開始");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("D項目接口調用===結束");
latch.countDown();
}
});
System.out.println("B C D start");
B.start();
C.start();
D.start();
latch.await();
System.out.println("<=================A項目方法結束==============>");
}
結果
<=================A項目方法開始==============>
B C D start
B項目接口調用===開始
C項目接口調用===開始
D項目接口調用===開始
D項目接口調用===結束
C項目接口調用===結束
B項目接口調用===結束
<=================A項目方法結束==============>