5

I have a non singleton actor that is creating an object that I want created only only once. How is this achieved? Can an actor be singleton? If yes, how?

class NonSingletonActor extends UntypedActor {

   public static onReceive(Object arg)  throws Exception {

         *block of code that needs to be executed once!*
} }
user_mda
  • 18,148
  • 27
  • 82
  • 145
  • Generally you can use a sychronized int runCount and an runCountMutex; getter, and incremetor for runCount use the ruNCountMutex object, and all code that deal with it, e.g around the if (runCount < 1) { do code}. But maybe there is an explicit aka technic for that – AlexWien Sep 24 '15 at 17:57
  • I dont understand, wouldnt the block of code in question be executed in parallel? in that case how would the count work? can ypou post the xemple in answer? – user_mda Sep 24 '15 at 17:58
  • see below at answer of Stanislav. I would have used an explicit self created object as mutex, he used the (NonSingletonActor.class object.) – AlexWien Sep 24 '15 at 18:44

3 Answers3

10

An AtomicBoolean is great for this, and in particular its compareAndSet method. That method takes two arguments: an "expected" value and a new value. It atomically does three things: (a) checks whether the current value is equal to the expected value, (b) if so, updates the current value to the new value, and (c) returns true iff that update happened (that is, if the expected and old values were the same).

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

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

This code will check to see if hasRun has a "false" value (which is its initial state). If so, it'll set itself to "true" and run if's block; otherwise, it'll keep its current state and not run the if's block. Crucially, the check-and-set is atomic (as the class name implies), meaning that no two threads can simultaneously see a false value; one of them will see the false value and set it to true, without the other thread being able to "sneak in" between those two actions.

yshavit
  • 42,327
  • 7
  • 87
  • 124
3

You can use synchronized block, with sync on class itself and use a boolean flag like:

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!*
    }
  } 
}

If you can get an exception in your block of code and want to be able to execute it one more time, then you can set a flag to true only on successed object creation.

Stanislav
  • 27,441
  • 9
  • 87
  • 82
  • onReceive is an overridden method. Will this be still possible? – user_mda Sep 24 '15 at 18:34
  • No, in that case you can use a synchronized block inside of it – Stanislav Sep 24 '15 at 18:35
  • Updated my answer, now it uses a block syncronization – Stanislav Sep 24 '15 at 18:39
  • 2
    shouldn't the flag be static? also, you may use double-checked locking `if(!executed){ synchronized(lock){ if(!executed) ...` and the main execution code can be done outside the sync block. – ZhongYu Sep 24 '15 at 18:49
  • 1
    If `executed` is always accessed (for both reads and writes) inside the synchronized block, it doesn't need to be volatile; the synchronization provides a happens-before edge. You would only need to make it volatile if you wanted to check `executed` outside of the synchronized block, in a sort of double-checked-lock pattern. – yshavit Sep 24 '15 at 18:50
  • I would still do the main work outside of the synch. Something like this: https://gist.github.com/yshavit/ab9160bfc59588b4ce7d – yshavit Sep 24 '15 at 19:38
  • @yshavit first of all, we need to know, what happens in that block of code. It''said, that there is an object creation. We don't know at the moment, how is this object used. With your example, it's possible that this object will be null after recieved method is already executed. – Stanislav Sep 25 '15 at 03:12
3

These other answers seem to have neglected the fact that your question is asked in the context of an Akka actor.

If you have an object that needs to be referenced from multiple actors but only created once, you should delegate those operations off to another actor—the actor which will create and manage the object in question—and then share the reference to that actor.

When a NonSingletonActor needs to operate on this object, it will send a message to the actor that contains that object. I would be extremely hesitant to mix Akka with the other solutions shown here, I expect you would see some unexpected behavior.

Ryan
  • 7,227
  • 5
  • 29
  • 40