CountDownLatch应用举例

定义

CountDownLatch是juc下的一个多线程锁,下面是jdk对它的定义

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

翻译如下

一种同步辅助工具,允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。

可以简单地理解为倒计次数锁,只有计数为零时,才能执行之后的代码

关键api

  1. 构造方法
public CountDownLatch(int count)
  1. 倒计数。每执行一次这个方法,计数就减少一次
public void countDown()
  1. 等待。阻塞方法,如果倒计次数没有清空,则会一直阻塞,后面的代码则无法执行
public void await() throws InterruptedException
public boolean await(long timeout, TimeUnit unit)
       throws InterruptedException
  1. 获取当前计数
public long getCount()

应用场景

有一个物业经理,派出手下11个员工去收取物业费,每个员工收费结束交给经理,经理计算手中的总额,大于等于100元,则把交给上级。最后的员工到达后,不论多少,经理都将手中的钱交给上级

EMPLOYEE_COUNT :员工数量
totalAmount :经理手中的金额
THRESHOLD_AMOUNT:门槛金额,当到达门槛金额时,要执行上交操作,totalAmount 清零
feeQueue :使用BlockingQueue阻塞队列用于存放收取的物业费

代码如下

private static final int EMPLOYEE_COUNT = 11;
private static final int THRESHOLD_AMOUNT = 100;
private static BlockingQueue<Integer> feeQueue = new ArrayBlockingQueue<>(EMPLOYEE_COUNT);
private static AtomicInteger totalAmount = new AtomicInteger(0);

public static void main(String[] args) {
    
    
    ExecutorService executorService = Executors.newFixedThreadPool(EMPLOYEE_COUNT + 1);
    CountDownLatch countDownLatch = new CountDownLatch(EMPLOYEE_COUNT);
    // 启动经理线程
    executorService.execute(() -> {
    
    
        try {
    
    
            while (countDownLatch.getCount() > 0 || !feeQueue.isEmpty()) {
    
    
                Integer amount = feeQueue.poll();
                if (amount != null) {
    
    

                    int currentAmount = totalAmount.addAndGet(amount);
                    System.out.println("经理收到了 " + amount + " 元,总金额:" + currentAmount);

                    if (currentAmount >= THRESHOLD_AMOUNT) {
    
    
                        System.out.println("经理将 " + currentAmount + " 元交给上级");
                        totalAmount.set(0);
                    }
                }
            }
        } catch (Exception e) {
    
    
            Thread.currentThread().interrupt();
        }
    });

    // 启动员工线程
    for (int i = 1; i <= EMPLOYEE_COUNT; i++) {
    
    
        final int employeeId = i;
        executorService.execute(() -> {
    
    
            int amount = (int) (Math.random() * 100) + 1; // 模拟收取随机金额
            System.out.println("员工 " + employeeId + " 收取了 " + amount + " 元");
            try {
    
    
                Thread.sleep(300);
                feeQueue.put(amount);
            } catch (InterruptedException e) {
    
    
                Thread.currentThread().interrupt();
            }
            countDownLatch.countDown();
        });
    }

    // 关闭线程池
    executorService.shutdown();
    try {
    
    
        countDownLatch.await(); // 等待所有员工线程完成

        if (executorService.awaitTermination(20, TimeUnit.SECONDS)) {
    
    
            System.out.println("线程执行完毕");
        } else {
    
    
            System.out.println("线程超时");
        }
    } catch (InterruptedException e) {
    
    
        Thread.currentThread().interrupt();
    }

    // 最后处理剩余金额
    int currentAmount = totalAmount.get();
    if (currentAmount > 0) {
    
    
        System.out.println("最后的员工到达后,经理将剩余的 " + currentAmount + " 元交给上级");
    }
}

输出结果如下
可以看到经理每收取100元就上交一次,最后的员工到达后,经理将剩余的钱上交了,符合预期

员工 11 收取了 29 元
员工 4 收取了 73 元
员工 6 收取了 54 元
员工 5 收取了 94 元
员工 3 收取了 19 元
员工 9 收取了 44 元
员工 1 收取了 57 元
员工 8 收取了 92 元
员工 7 收取了 23 元
员工 2 收取了 83 元
员工 10 收取了 18 元
经理收到了 19 元,总金额:19
经理收到了 29 元,总金额:48
经理收到了 23 元,总金额:71
经理收到了 92 元,总金额:163
经理将 19+29+23+92=163 元交给上级
经理收到了 44 元,总金额:44
经理收到了 73 元,总金额:117
经理将 44+73=117 元交给上级
经理收到了 18 元,总金额:18
经理收到了 83 元,总金额:101
经理将 18+83=101 元交给上级
经理收到了 57 元,总金额:57
经理收到了 94 元,总金额:151
经理将 57+94=151 元交给上级
经理收到了 54 元,总金额:54
线程执行完毕
最后的员工到达后,经理将剩余的 54 元交给上级