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

use*_*349 11 java concurrency multithreading

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

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

以下是我的方法:

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

这可能吗?我正在使用Java 7.

Oli*_*ain 11

@ 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");
  }
}
Run Code Online (Sandbox Code Playgroud)

以上假设您不必等待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");
  }
}
Run Code Online (Sandbox Code Playgroud)

这样做的原因是AtomicBoolean.getAndSet(true),在一个原子操作中,将返回先前设置的值并使新值成为true.因此,false返回到您的方法的第一个线程将返回(因为该变量被初始化为false),并且它将原子地将其设置为true.由于第一个线程返回false,它将占用if语句中的第一个分支,并且您的初始化将会发生.任何其他调用都会发现自第一个线程将其设置为true后wasRun.getAndSet返回true,因此它们将获取第二个分支,您将获得所需的日志消息.

CountDownLatch初始化为1,因此除了第一次调用之外的所有线程await.它们将阻塞,直到第一个线程调用countDown,将计数设置为0,释放所有等待的线程.