如何保证一段代码在多线程环境下只执行一次?

How to ensure a block of code is executed only once in a multithreaded environment?

我有一个非单例 actor,它正在创建一个我只想创建一次的对象。这是如何实现的?演员可以是单身吗?如果是,如何?

class NonSingletonActor extends UntypedActor {

   public static onReceive(Object arg)  throws Exception {

         *block of code that needs to be executed once!*
} }

您可以使用同步块,同步 class 本身并使用布尔标志,如:

class NonSingletonActor extends UntypedActor {

  private static volatile boolean executed;

  public static onReceive(Object arg)  throws Exception {
    if (executed)
      return;

    synchronized (NonSingletonActor.class) {
      if (executed)
        return; 

      executed= true; 
      *block of code that needs to be executed once!*
    }
  } 
}

如果您可以在代码块中遇到异常并希望能够再次执行它,那么您可以仅在成功创建对象时将标志设置为 true。

AtomicBoolean 非常适合这个,尤其是它的 compareAndSet 方法。该方法有两个参数:一个 "expected" 值和一个新值。它以原子方式做三件事:(a) 检查当前值是否等于预期值,(b) 如果是,则将当前值更新为新值,以及 (c) returns true iff 更新发生(也就是说,如果预期值和旧值相同)。

private static final AtomicBoolean hasRun = new AtomicBoolean(false);

...
if (hasRun.compareAndSet(false, true)) {
    // your code here
}

此代码将检查 hasRun 是否具有 "false" 值(这是其初始状态)。如果是这样,它将自己设置为 "true" 和 运行 if 的块;否则,它将保持其当前状态而不是 运行 if 的块。至关重要的是,检查和设置是原子的(正如 class 名称所暗示的那样),这意味着没有两个线程可以同时看到假值;其中一个将看到 false 值并将其设置为 true,而另一个线程无法在这两个操作之间 "sneak in"。

这些其他答案似乎忽略了这样一个事实,即您的问题是在 Akka 演员的背景下提出的。

如果您有一个对象需要从多个参与者引用但只创建一次,您应该将这些操作委托给另一个参与者——将创建和管理相关对象的参与者——然后共享引用给那个演员。

当一个NonSingletonActor需要对该对象进行操作时,它会向包含该对象的actor发送消息。我会非常犹豫将 Akka 与此处显示的其他解决方案混合使用,我希望您会看到一些意想不到的行为。