[关闭]
@adamhand 2019-01-21T18:58:47.000000Z 字数 4526 阅读 767

Java并发之CountDownLatch、CyclicBarrier和Semaphore


CountDownLatch

是什么

先看一下官方的解释:

  1. CountDownLatch:A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

大概的意思是,CountDownLatch允许一个或多个线程等待其他一组线程完成后,再继续执行。其实这从CountDownLatch的名字也可以大概看出它的用途,count-计数,down-减,latch-门栓,当计数器减为零之后,门栓就打开了,线程可以继续执行。

可以举个例子:在玩LOL英雄联盟时会出现十个人不同加载状态,但是最后一个人由于各种原因始终加载不了100%,于是游戏系统自动等待所有玩家的状态都准备好,才展现游戏画面。

怎么用

在使用时关注的方法主要有三个:

  1. await(); // 使当前线程在锁存器倒计数至零之前一直等待
  2. await(long timeout, TimeUnit unit) throws InterruptedException { }; //和await()类似,只不过等待timeout的时间后计数器值还没变为0的话就会继续执行
  3. countDown(); // 递减锁存器的计数,如果计数到达零,则释放所有等待的线程

下面以上面提到的的游戏的场景,模拟一下CountDownLatch的用法。

  1. public class CountDownLatchGame {
  2. public static void main(String[] args) {
  3. ExecutorService executor = Executors.newCachedThreadPool();
  4. final CountDownLatch latch = new CountDownLatch(3);
  5. executor.execute(new Runnable() {
  6. @Override
  7. public void run() {
  8. try {
  9. latch.await();
  10. System.out.println("所有玩家都准备好,可以开始游戏了");
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. });
  16. executor.execute(new Runnable() {
  17. @Override
  18. public void run() {
  19. System.out.println("玩家1已经准备好");
  20. latch.countDown();
  21. }
  22. });
  23. executor.execute(new Runnable() {
  24. @Override
  25. public void run() {
  26. System.out.println("玩家2已经准备好");
  27. latch.countDown();
  28. }
  29. });
  30. executor.execute(new Runnable() {
  31. @Override
  32. public void run() {
  33. System.out.println("玩家3已经准备好");
  34. latch.countDown();
  35. }
  36. });
  37. }
  38. }

打印结果如下:

  1. 玩家1已经准备好
  2. 玩家2已经准备好
  3. 玩家3已经准备好
  4. 所有玩家都准备好,可以开始游戏了

待续

源码分析

CyclicBarrier

是什么

先看一下官方的解释:

  1. CyclicBarrierA synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.

也就是说,CyclicBarrier允许一组线程相互之间等待,达到一个共同点(Barrier,栅栏),再继续执行。

举一个打游戏的例子,打游戏的时候需要等待玩家都准备好之后才会开始游戏,所以游戏玩家之间需要相互等待。

怎么用

下面就上面的打游戏的例子写一下代码。

  1. public class CyclicBarrierDemo {
  2. public static void main(String[] args) {
  3. ExecutorService executor = Executors.newCachedThreadPool();
  4. final CyclicBarrier barrier = new CyclicBarrier(5, new Runnable() {
  5. @Override
  6. public void run() {
  7. System.out.println("玩家准备好,游戏开始");
  8. }
  9. });
  10. playerWait(barrier, executor);
  11. /**
  12. * 可重用
  13. */
  14. //playerWait(barrier, executor);
  15. //playerWait(barrier, executor);
  16. }
  17. public static void playerWait(final CyclicBarrier barrier, ExecutorService executor){
  18. for (int i = 0; i < 5; i++){
  19. final int j = i;
  20. executor.execute(new Runnable() {
  21. @Override
  22. public void run() {
  23. try {
  24. System.out.println("玩家"+j+"准备好,等待其他玩家准备");
  25. barrier.await();
  26. } catch (InterruptedException e) {
  27. e.printStackTrace();
  28. } catch (BrokenBarrierException e) {
  29. e.printStackTrace();
  30. }
  31. }
  32. });
  33. }
  34. }
  35. }

打印结果为:

  1. 玩家1准备好,等到其他玩家准备
  2. 玩家4准备好,等到其他玩家准备
  3. 玩家3准备好,等到其他玩家准备
  4. 玩家0准备好,等到其他玩家准备
  5. 玩家2准备好,等到其他玩家准备
  6. 玩家准备好,游戏开始

另外,CyclicBarrier是可以重用的,当线程执行结束后,又可以用来进行新一轮的使用。而CountDownLatch无法进行重复使用。将上面程序中的注释打开,即可以重用CyclicBarrier。

待续

源码分析

Semaphore

是什么

Semaphore翻译成字面意思为 信号量,Semaphore可以控同时访问的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。

Semaphore提供了2个构造器:

  1. public Semaphore(int permits) { //参数permits表示许可数目,即同时可以允许多少线程进行访问
  2. sync = new NonfairSync(permits);
  3. }
  4. public Semaphore(int permits, boolean fair) { //这个多了一个参数fair表示是否是公平的,即等待时间越久的越先获取许可
  5. sync = (fair)? new FairSync(permits) : new NonfairSync(permits);
  6. }

Semaphore类中比较重要的几个方法,首先是acquire()、release()方法:

  1. public void acquire() throws InterruptedException { } //获取一个许可,若无许可能够获得,则会一直等待,直到获得许可。
  2. public void acquire(int permits) throws InterruptedException { } //获取permits个许可
  3. public void release() { } //释放一个许可
  4. public void release(int permits) { } //释放permits个许可

这4个方法都会被阻塞,如果想立即得到执行结果,可以使用下面几个方法:

  1. public boolean tryAcquire() { }; //尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
  2. public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { }; //尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回fals
  3. public boolean tryAcquire(int permits) { }; //尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
  4. public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException { }; //尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false

另外还可以通过availablePermits()方法得到可用的许可数目。

怎么用

下面通过一个例子来看一下Semaphore的具体使用:

假若一个工厂有5台机器,但是有10个工人,一台机器同时只能被一个工人使用,只有使用完了,其他工人才能继续使用。

  1. public class SemaphoreDemo {
  2. public static void main(String[] args) {
  3. int workerNum = 10;
  4. final Semaphore semaphore = new Semaphore(5);
  5. ExecutorService executor = Executors.newCachedThreadPool();
  6. for(int i = 0; i < workerNum; i++){
  7. final int j = i;
  8. executor.execute(new Runnable() {
  9. @Override
  10. public void run() {
  11. try {
  12. semaphore.acquire();
  13. System.out.println("工人"+j+"正在使用机器");
  14. Thread.sleep(1000);
  15. System.out.println("工人"+j+"释放机器");
  16. semaphore.release();
  17. } catch (InterruptedException e) {
  18. e.printStackTrace();
  19. }
  20. }
  21. });
  22. }
  23. }
  24. }

打印结果为:

  1. 工人0正在使用机器
  2. 工人3正在使用机器
  3. 工人2正在使用机器
  4. 工人1正在使用机器
  5. 工人4正在使用机器
  6. 工人2释放机器
  7. 工人0释放机器
  8. 工人3释放机器
  9. 工人7正在使用机器
  10. 工人1释放机器
  11. 工人4释放机器
  12. 工人9正在使用机器
  13. 工人8正在使用机器
  14. 工人6正在使用机器
  15. 工人5正在使用机器
  16. 工人8释放机器
  17. 工人7释放机器
  18. 工人9释放机器
  19. 工人6释放机器
  20. 工人5释放机器

待续

源码分析

参考

CountDownLatch用法
你真的理解CountDownLatch与CyclicBarrier使用场景吗?
Java并发编程:CountDownLatch、CyclicBarrier和Semaphore
Java多线程编程-(8)-两种常用的线程计数器CountDownLatch和循环屏障CyclicBarrier
Java并发编程:CountDownLatch、CyclicBarrier和Semaphore

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注