多个前端发送相同请求时如何让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的
isRequestAlreadyRunning是方法局部变量,每个请求进来都会创建独立的实例,状态完全不共享,根本起不到跨请求互斥的作用。 - 方案2的
AtomicBoolean和CountDownLatch同样是方法局部变量,每个请求操作的是自己的状态,CAS校验自然不会生效,而且你写的await逻辑不符合需求:你要的是直接拒绝其余请求,而不是让其余请求排队等待前一个请求处理完成。
最终方案合理性说明
你最终写的方案逻辑是正确的,核心优化点有两个:
- 把
AtomicBoolean改成了类成员变量,所有请求共享同一个状态实例,CAS操作的是同一个标识,保证同一时间只有1个请求能通过校验。 - 加了
finally块重置状态,就算处理逻辑中途抛出异常,也不会导致标识永远为true、后续所有请求都被拦截的问题。
注意修正你最终代码里的笔误:if判断里写的变量名是updateStarted,实际定义的变量是isRequestAlreadyRunning,统一变量名即可正常运行。
内容的提问来源于stack exchange,提问作者ctrlaltdeleon
相关产品推荐
相关产品推荐

