1

我想做什么?

当 2+ 个前端通过套接字发出请求某些内容时,我只希望后端处理其中 1 个请求并拒绝其他请求。

当前尝试执行此操作的代码是什么?

后端代码。

// Original code.

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

  // Thinking of putting some "locking" code around this if statement?
  // Something like "If this function is already in process, deny other requests and remove them"?
  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("New adventure already exists and hasn't ended yet!");
    }
  } else {
    message.append("Request formatting is invalid!");
  }

  return new AdventureIoMessage(adventures, createRequest, message); 
}
// My suggested code 1 using a 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("New adventure already exists and hasn't ended yet!");
      }
    } else {
      message.append("Request formatting is invalid!");
    }
    isRequestAlreadyRunning = false; // Done processing.
  }

  return new AdventureIoMessage(adventures, createRequest, message); 
}
// My suggested code 2 using AtomicBoolean and 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("New adventure already exists and hasn't ended yet!");
      }
    } else {
      message.append("Request formatting is invalid!");
    }
    updateFinished.countDown();
  } else {
    updateFinished.await();
  }

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

对于建议的代码 2,也尝试了AtomicBoolean唯一的。

我期望结果是什么?

我考虑保留某种变量来检查是否create()应该仍然发生,期望只有 1 个请求通过。

实际结果是什么?

当 2+ 个前端通过套接字发出请求某些东西时,create()尽管内部有检查逻辑,但其中有 2 个会发生。

我认为问题可能是什么?

一个时间问题,因为 2+ 前端可以create()在没有适当检查的情况下同时访问此方法。代码的原始逻辑在它们不会同时触发的过程中起作用。我希望这是有道理的,因为很难说这对 Java 来说是新事物。

编辑:

我最终确定的工作解决方案是:

// Within a class...

AtomicBoolean isRequestAlreadyRunning = new AtomicBoolean();

public IoMessage create(IoMessage request) {
  Adventure[] adventures = null;
  AdventureIoMessage createRequest = null;
  StringBuilder message = new StringBuilder();
  
  if (updateStarted.compareAndSet(false, true) {
    try {
      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("New adventure already exists and hasn't ended yet!");
        }
      } else {
        message.append("Request formatting is invalid!");
      }
    } finally {
      isRequestAlreadyRunning.set(false);
    }
  }

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

1 回答 1

1

问题:在 Java 中,每个方法都有自己的堆栈,因此在方法内部分配的所有变量都是唯一的。

解决方案:

  • 把你的isRequestAlreadyRunning作为成员变量,而不是方法变量
  • 成功boolean isRequestAlreadyRunning,不Boolean isRequestAlreadyRunning
  • 做它volatile,就像在private volatile boolean isRequestAlreadyRunning

那么你的第一次尝试应该会奏效。

由于同样的原因,您的第二次尝试将不起作用。

或者,搜索 Java mutex,如“互斥”,除了sychronized(myLockObj)语句之外,您会发现许多专门用于此的类和技术。

于 2021-09-01T19:49:01.550 回答