快速上手CountDownLatch
CountDownLatch可以使一个线程在等待其他多个线程各自执行完毕后再执行。也就是说countdownlatch可以实现线程等待。
实现原理
countdownlatch 内部定义了一个计数器和一个等待队列,在计数器递减到0之前,等待队列里的现成是阻塞状态,当计数器到0后,会去唤醒此时等待队列里所有的阻塞线程
主要api
CountDownLatch(int count); //设置计数器的count值
await();//阻塞当前线程,加入其进入阻塞队列
await(long timeout, TimeUnit unit); //阻塞当前线程,当超时时间到时,该线程开始执行
countDown(); // 计数器减1
api的实现
创建计数器
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
阻塞线程
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//锁重入次数大于0 则新建节点加入阻塞队列,挂起当前线程
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
计时器递减
public final boolean releaseShared(int arg) {
//递减锁的重入次数
if (tryReleaseShared(arg)) {
doReleaseShared();//唤醒队列所有阻塞的节点
return true;
}
return false;
}
private void doReleaseShared() {
//唤醒所有阻塞队列里面的线程
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {//节点是否在等待唤醒状态
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))//修改状态为初始
continue;
unparkSuccessor(h);//成功则唤醒线程
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
使用场景
countdownlatch 可以用来在业务中,需要等待多个操作执行完的才触发返回的case。
举个实际点的例子,淘宝客服这边使用的客服工作台,用户进线后,需要根据用户发送过来的订单卡片信息,来给客服展现相关的用户信息、用户画像、历史售后以及订单商品详情等等的信息,辅助客服来解决用户的问题。这些信息都来源于不同的内部技术组,比如拿订单信息来说,就可能在使用订单号获取订单信息的这个服务化接口实现中,调用订单组的接口提供订单主体信息,调用商品组的接口获取商品的信息,调用仓储组的接口获取订单商品的仓库信息,调用库存组获取该商品当前的库存信息等等,在一个接口中,如果一个一个接口的调用写下来,那么这些接口的调用就变成了串行,服务化的接口大部分都耗费在网络传输上,所以,就要想办法如何将这些接口的调用并行化,这里就可以使用countdownlatch来实现了,并行的调用各个组的服务化接口获取结果,当各个组的服务化接口全部返回后,触发countdownlatch计数器递减到0,此时,整个客服工作台的接口返回,这样就提供了整个接口的响应时间。
代码实现
@Service
@AllArgConstructor
public class OrderFacade {
private OrderCompose orderCompose;
private ItemCompose itemCompose;
private WarehouseCompose warehouseCompose;
private StorageCompose storageCompose;
public Order getOrderItemByOrderItemId(String orderId){
OrderResult result = null;
Executor executor = Executors.newFixedThreadPool(4);
CountDownLatch latch = new CountDownLatch(4);
executor.execute(() -> {
Order order = orderCompose.getOrder(orderId);
latch.countDown();
});
executor.execute(() -> {
Item item = itemCompose.getSku(orderId);
latch.countDown();
});
executor.execute(() -> {
Warehouse warehouse = warehouseCompose.getWarehouse(orderId);
latch.countDown();
});
executor.execute(() -> {
Storage storage = storageCompose.getStorage(orderId);
latch.countDown();
});
latch.await();
result = buildOrder(order, item, warehouse, storage);
return result;
}
}