CountDownLatch - 线程计数器
包名:java.util.concurrent
功能:
多线程编程中,要并发请求10个接口,等这些接口都返回结果再进行统一处理后,将结果返回。
调用countDown() 方法 ,计数减去 1。
代码示例
调用countDown()方法后,会执行下面的源码
//此处需要maven 引入 commons-lang3 包
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.annotation.Async;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
public class LockTest {
final CountDownLatch cdl = new CountDownLatch(10);
@Test
void testLatch() throws InterruptedException {
//定义固定线程池
ExecutorService exec = Executors.newFixedThreadPool(10);
List<String> list = new ArrayList<>();
//统计执行时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
for (int i = 0; i < 10; i++) {
exec.execute(new Runnable() {
@Override
public void run() {
int sleep = RandomUtils.nextInt(1,10);
try {
System.out.println("当前线程:" + Thread.currentThread().getName() + ",休眠:" + sleep);
//此处模拟请求外部接口
TimeUnit.SECONDS.sleep(sleep);
//此处模拟返回结果
list.add(Thread.currentThread().getName() + "-" + sleep);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//执行完一次,从计数器中释放线程
cdl.countDown();
}
});
}
//等待所有线程执行完
cdl.await();
//统一处理结果
for (String s : list) {
System.out.println(s);
}
stopWatch.stop();
System.out.println("总计耗时:" + stopWatch.getTime() + "ms");
}
}
执行结果:
当前线程:pool-1-thread-8,休眠:1
当前线程:pool-1-thread-2,休眠:3
当前线程:pool-1-thread-9,休眠:9
当前线程:pool-1-thread-7,休眠:2
当前线程:pool-1-thread-4,休眠:8
当前线程:pool-1-thread-3,休眠:4
当前线程:pool-1-thread-1,休眠:5
当前线程:pool-1-thread-6,休眠:7
当前线程:pool-1-thread-10,休眠:4
当前线程:pool-1-thread-5,休眠:6
pool-1-thread-8-1
pool-1-thread-7-2
pool-1-thread-2-3
pool-1-thread-10-4
pool-1-thread-3-4
pool-1-thread-1-5
pool-1-thread-5-6
pool-1-thread-6-7
pool-1-thread-4-8
pool-1-thread-9-9
总计耗时:9010ms
CyclicBarrier - 等待至barrier状态再全部同时执行(回环栅栏)
包名:java.util.concurrent
功能:
一组线程,等待他们各自执行完操作并且都调用 await() 方法,再同时执行各自后续的任务。
代码示例:
//此处需要maven 引入 commons-lang3 包
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.annotation.Async;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
public class LockTest {
CyclicBarrier barrier = new CyclicBarrier(5);
@Test
void testBarrier() throws InterruptedException {
for (int i = 0; i < 5; i++) {
//启动5个线程
new Tests(barrier).start();
}
Thread.sleep(5000);
System.out.println("继续操作");
}
static class Tests extends Thread {
CyclicBarrier cyclicBarrier;
Tests(CyclicBarrier barrier) {
cyclicBarrier = barrier;
}
@Override
public void run() {
//模拟执行代码时长
int r = RandomUtils.nextInt(1,5);
System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + "线程休眠:" + Thread.currentThread().getName() + ": " + r + "s");
try {
TimeUnit.SECONDS.sleep(r);
//调用await()方法,线程处于barrier,等待所有线程都调用await()方法
cyclicBarrier.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
//所有线程都调用await()方法后,执行后续的代码
System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + " 所有执行完成,继续向下执行: " + Thread.currentThread().getName());
}
}
}
执行结果:
2023-08-31 12:32:46线程休眠:Thread-2: 3s
2023-08-31 12:32:46线程休眠:Thread-4: 4s
2023-08-31 12:32:46线程休眠:Thread-3: 1s
2023-08-31 12:32:46线程休眠:Thread-1: 3s
2023-08-31 12:32:46线程休眠:Thread-5: 1s
2023-08-31 12:32:50 所有执行完成,继续向下执行: Thread-4
2023-08-31 12:32:50 所有执行完成,继续向下执行: Thread-1
2023-08-31 12:32:50 所有执行完成,继续向下执行: Thread-5
2023-08-31 12:32:50 所有执行完成,继续向下执行: Thread-3
2023-08-31 12:32:50 所有执行完成,继续向下执行: Thread-2
继续操作
结论
相同点:
CountDownLatch 和 CyclicBarrier 都能够实现线程之间的等待
区别
CountDownLatch 一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行
CyclicBarrier 一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行
CountDownLatch 是不能够重用的,而 CyclicBarrier 是可以重用的。