前言
主要记录下CountDownLatch、CyclicBarrer、Semaphore 用法
CountDownLatch
经常遇到一种场景,线程还未执行完毕,主函数执行完了,为了避免这种情况发生可以使用countDownLatch 计数器,会阻塞主函数,等待线程执行完毕后,才放行
- 测试
public static void main(String[] args) throws InterruptedException {
CountDownLatch downLatch = new CountDownLatch(5);
for (int i = 0; i < 5; i++) {
final int num = i;
new Thread(()->{
System.out.println("线程"+Thread.currentThread().getName()+" "+(num+1));
downLatch.countDown();
},String.valueOf(num+1)).start();
}
downLatch.await();
System.out.println("线程"+Thread.currentThread().getName()+" 执行完毕。。。");
}
countDownLatch 会阻塞线程,计数器依次递减,减到0时,放行,执行后续代码
CyclicBarrer
加方计数器,与CountDownLatch 想反,当满足某种条件时才执行后续步骤
- 测试
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> {
System.out.println("放行 cyclicBarrier...");
});
for (int i = 0; i < 5; i++) {
final int num = i;
new Thread(() -> {
System.out.println("线程" + Thread.currentThread().getName());
try {
cyclicBarrier.await(); //执行一次,+1,阻塞等待,达到计数器后,解除阻塞
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},String.valueOf(num + 1)).start();
System.out.println("等待执行:"+cyclicBarrier.getNumberWaiting());
}
CyclicBarrer未达到设置的数量时,cyclicBarrier.await()
一直会阻塞,达到后放行,
Semaphore
信号量计数器,一次只能是指定个数线程,其他线程等待,线程释放后,其他线程才可进入,常用场景限流
- 测试
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2); //通行信号量
for (int i = 0; i < 6; i++) {
final int num = i;
new Thread(() -> {
try {
semaphore.acquire(); //获取控制权信号量-1
System.out.println("线程" + Thread.currentThread().getName() + "获得控制权");
System.err.println("线程" + Thread.currentThread().getName() + "释放控制权");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(); //释放控制权,信号量 +1
}
},String.valueOf(num+1)).start();
}
}
设置一次通行2个线程,6个线程,只有其他线程放弃控制权,后续线程才可获得
总结
- CountDownLatch:减法计数器,归零后解除阻塞
- CyclicBarrer: 加法计数器,达到设定值,解除阻塞,放行
- Seamphore: 信号量计数器,一次执行通过指定线程,线程获取信后,信号量-1,归零时阻塞,释放后,信号量后+1,信号量>0时不阻塞。