You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多个前端发送相同请求时如何让Java后端仅处理首个请求并忽略其余

问题梳理

核心需求

当2个及以上前端通过socket emit发送请求时,后端仅处理其中1个请求,拒绝其余所有请求。

历史尝试代码

原始代码

public IoMessage create(IoMessage request) {
  Adventure[] adventures = null;
  AdventureIoMessage createRequest = null;
  StringBuilder message = new StringBuilder();

  // 想在该if语句周围加锁逻辑:如果函数已在处理中,直接拒绝其他请求
  if (request instanceof AdventureIoMessage) {
    createRequest = (AdventureIoMessage) request;
    DatabaseManager databaseManager = Application.getDatabaseManager();
    Adventure adventure = Adventure.read(databaseManager);
    if (adventure == null || adventure.getStartTime() == 0 || !adventure.isActive()) {
      createAdventure(adventures, createRequest, message);
    } else {
      message.append("新的冒险已经存在且尚未结束!");
    }
  } else {
    message.append("请求格式无效!");
  }

  return new AdventureIoMessage(adventures, createRequest, message); 
}

方案1:使用Boolean变量做校验

public IoMessage create(IoMessage request) {
  Adventure[] adventures = null;
  AdventureIoMessage createRequest = null;
  StringBuilder message = new StringBuilder();
  Boolean isRequestAlreadyRunning = false;
  
  if (!isRequestAlreadyRunning) {
    isRequestAlreadyRunning = true;
    if (request instanceof AdventureIoMessage) {
      createRequest = (AdventureIoMessage) request;
      DatabaseManager databaseManager = Application.getDatabaseManager();
      Adventure adventure = Adventure.read(databaseManager);
      if (adventure == null || !adventure.isActive()) {
        createAdventure(adventures, createRequest, message);
      } else {
        message.append("新的冒险已经存在且尚未结束!");
      }
    } else {
      message.append("请求格式无效!");
    }
    isRequestAlreadyRunning = false; // 处理完成重置状态
  }

  return new AdventureIoMessage(adventures, createRequest, message); 
}

方案2:使用AtomicBoolean+CountDownLatch做校验

public IoMessage create(IoMessage request) {
  Adventure[] adventures = null;
  AdventureIoMessage createRequest = null;
  StringBuilder message = new StringBuilder();
  AtomicBoolean updateStarted = new AtomicBoolean();
  CountDownLatch updateFinished = new CountDownLatch(1);
  
  if (updateStarted.compareAndSet(false, true)) {
    if (request instanceof AdventureIoMessage) {
      createRequest = (AdventureIoMessage) request;
      DatabaseManager databaseManager = Application.getDatabaseManager();
      Adventure adventure = Adventure.read(databaseManager);
      if (adventure == null || !adventure.isActive()) {
        createAdventure(adventures, createRequest, message);
      } else {
        message.append("新的冒险已经存在且尚未结束!");
      }
    } else {
      message.append("请求格式无效!");
    }
    updateFinished.countDown();
  } else {
    updateFinished.await();
  }

  return new AdventureIoMessage(adventures, createRequest, message); 
}

针对方案2,也尝试过仅使用AtomicBoolean实现。

现象描述

  • 预期:通过变量校验控制create()方法仅允许1个请求执行,其余请求直接拒绝
  • 实际:并发请求场景下,仍有多个请求同时执行了create()逻辑,非并发场景下逻辑正常
  • 初步判断:并发时序问题,同时进来的请求没有被有效拦截

解答

历史方案失效原因

  1. 方案1的isRequestAlreadyRunning是方法局部变量,每个请求进来都会创建独立的实例,状态完全不共享,根本起不到跨请求互斥的作用。
  2. 方案2的AtomicBoolean和CountDownLatch同样是方法局部变量,每个请求操作的是自己的状态,CAS校验自然不会生效,而且你写的await逻辑不符合需求:你要的是直接拒绝其余请求,而不是让其余请求排队等待前一个请求处理完成。

最终方案合理性说明

你最终写的方案逻辑是正确的,核心优化点有两个:

  1. 把AtomicBoolean改成了类成员变量,所有请求共享同一个状态实例,CAS操作的是同一个标识,保证同一时间只有1个请求能通过校验。
  2. 加了finally块重置状态,就算处理逻辑中途抛出异常,也不会导致标识永远为true、后续所有请求都被拦截的问题。

注意修正你最终代码里的笔误:if判断里写的变量名是updateStarted,实际定义的变量是isRequestAlreadyRunning,统一变量名即可正常运行。


内容的提问来源于stack exchange,提问作者ctrlaltdeleon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 02:21:02