Java实现一个等待异步返回的阻塞方法(便于同步方法等待异步返回结果)

此方法同样兼容jdk21的虚拟线程。

需要Guava库,或可利用其他缓存库代替:


@Slf4j
@NoArgsConstructor
public class AsyncResultLock<K> {

  private final Cache<K, Thread> lockMap = CacheBuilder.newBuilder()
      .expireAfterAccess(1, TimeUnit.MINUTES)
      .concurrencyLevel(Runtime.getRuntime().availableProcessors() + 1)
      .weakValues()
      .build();

  private final Cache<K, Object> cacheMap = CacheBuilder.newBuilder()
      .expireAfterAccess(10, TimeUnit.SECONDS)
      .concurrencyLevel(Runtime.getRuntime().availableProcessors() + 1)
      .build();

  public <T> T park(K key, long timeout, TimeUnit unit, Callable<T> before, Consumer<T> after) throws Exception {
    lockMap.put(key, Thread.currentThread());
    T ret;
    try {
      ret = before.call();
    } catch (Exception e) {
      lockMap.invalidate(key);
      throw e;
    }
    LockSupport.parkNanos(TimeUnit.NANOSECONDS.convert(timeout, unit));
    @SuppressWarnings("unchecked")
    T cache = (T) cacheMap.getIfPresent(key);
    after.accept(cache);
    lockMap.invalidate(key);
    return ret;
  }

  public boolean unPark(K key, Object result) {
    Thread thread = lockMap.getIfPresent(key);
    if (thread != null) {
      cacheMap.put(key, result);
    }
    LockSupport.unpark(thread);
    return thread != null;
  }


}

 

使用方法:

阻塞方法:

  @SneakyThrows
  public void sendAndWait(String cameraId, MessageQo message, long timeout, TimeUnit unit, Consumer<MessageResult> consumer) {
    Channel ch = connectionPool.borrow(cameraId);
    if (message.getId() == null && ch.attr(ChannelAttr.COUNTER.getAttributeKey()).get() instanceof CircularCounter c) {
      int id = c.getAndIncrement();
      message.setId(id);
    }
    ASYNC_RESULT_LOCK.park(message.getId(), timeout, unit, () -> {
      try {
        String string = VZConstants.OBJECT_MAPPER.writeValueAsString(message);
        log.info("发送消息:->{} {}", cameraId, string);
        DataPacket dataPacket = new DataPacket((byte) 0, Unpooled.copiedBuffer(string, VZConstants.CHARSET));
        ch.writeAndFlush(dataPacket);
      } finally {
        connectionPool.revert(cameraId, ch);
      }
      return null;
    }, consumer);

  }

 异步响应得到结果时:

  private void consume(String key, VZCommand command, MessageResult result) {
    ASYNC_RESULT_LOCK.unPark(result.getId(), result);
  }

 

posted @ 2025-04-13 00:22  Jackie_JK  阅读(21)  评论(0)    收藏  举报