如何确保该方法仅执行一次并且仅从一个线程执行?

2022-09-04 02:38:20

我有一个以下方法,我想在以下条件下执行:

  • 此方法应仅执行一次。一旦它被执行,它就不能再次执行,所以如果有人试图再次执行,它应该通过记录一些有用的错误消息或任何有用的信息来返回。already executed
  • 它应该只由一个线程执行。那么如果多个线程正在调用下面的方法,那么它应该只由一个线程调用,其他线程应该等待初始化完成?

以下是我的方法:

  public void initialize() {
    List<Metadata> metadata = getMetadata(true);
    List<Process> process = getProcess();
    if (!metadata.isEmpty() && !process.isEmpty()) {
        Manager.setAllMetadata(metadata, process);
    }
    startBackgroundThread();
  }

这可以做到吗?我正在使用Java 7。


答案 1

@ShayHaned的解决方案使用锁定。您可以通过以下方式提高效率:AtomicBoolean

AtomicBoolean wasRun = new AtomicBoolean(false);
CountDownLatch initCompleteLatch = new CountDownLatch(1);

public void initialize() {
  if (!wasRun.getAndSet(true)) {
      List<Metadata> metadata = getMetadata(true);
      List<Process> process = getProcess();
      if (!metadata.isEmpty() && !process.isEmpty()) {
          Manager.setAllMetadata(metadata, process);
      }
      startBackgroundThread();
      initCompleteLatch.countDown();
  } else {
      log.info("Waiting to ensure initialize is done.");
      initCompleteLatch.await();
      log.warn("I was already run");
  }
}

以上假设您不必等待工作完成。如果这样做,解决方案将变为:startBackgroundThread

AtomicBoolean wasRun = new AtomicBoolean(false);
CountDownLatch initCompleteLatch = new CountDownLatch(1);

public void initialize() {
  if (!wasRun.getAndSet(true)) {
      List<Metadata> metadata = getMetadata(true);
      List<Process> process = getProcess();
      if (!metadata.isEmpty() && !process.isEmpty()) {
          Manager.setAllMetadata(metadata, process);
      }
      // Pass the latch to startBackgroundThread so it can
      // call countDown on it when it's done.
      startBackgroundThread(initCompleteLatch);
  } else {
      log.info("Waiting to ensure initialize is done.");
      initCompleteLatch.await();
      log.warn("I was already run");
  }
}

这样做的原因是,在一个原子操作中,它将返回先前设置的值,并使新值为 。因此,将返回第一个到达方法的线程(因为变量已初始化为 false),并且它将以原子方式将其设置为 true。由于第一个线程返回 false,它将采用语句中的第一个分支,并且将进行初始化。任何其他调用都会发现,由于第一个线程将其设置为 true,因此它们将采用第二个分支,您只会获得所需的日志消息。AtomicBoolean.getAndSet(true)truefalseifwasRun.getAndSettrue

CountDownLatch 初始化为 1,因此除第一个调用之外的所有线程都位于该进程上。它们将阻塞,直到第一个线程调用,这将把计数设置为0,释放所有等待的线程。awaitcountDown


答案 2

您可以为方法创建一个静态标志,该标志仅在调用该方法后才会更改。使用静态标志背后的想法是,它不属于它属于类的实例,这意味着从同一类创建的所有线程都将有权访问标志的相同值,因此一旦标志的布尔值在第一次调用方法后被更改,所有其他线程将被if else条件语句绕过。

 static boolen flag;
public void initialize() {
if (flag)
{// return from here or some message you want to generate 
}else{
    List<Metadata> metadata = getMetadata(true);
    List<Process> process = getProcess();
    if (!metadata.isEmpty() && !process.isEmpty()) {
        Manager.setAllMetadata(metadata, process);
    }
       flag = true;
    startBackgroundThread();       }}

我希望这能解决您的疑问


推荐