dimitar
dimitar

Reputation: 177

visibility of immutable object after publication

I have an immutable object, which is capsulated in class and is global state.

Lets say i have 2 threads that get this state, execute myMethod(state) with it. And lets say thread1 finish first. It modify the global state calling GlobalStateCache.updateState(state, newArgs);

GlobalStateCache {
   MyImmutableState state = MyImmutableState.newInstance(null, null);

   public void updateState(State currentState, Args newArgs){
      state = MyImmutableState.newInstance(currentState, newArgs);
   }
}

So thread1 will update the cached state, then thread2 do the same, and it will override the state (not take in mind the state updated from thread1)

I searched google, java specifications and read java concurrency in practice but this is clearly not specified. My main question is will the immutable state object value be visible to a thread which already had read the immutable state. I think it will not see the changed state, only reads after the update will see it.

So i can not understand when to use immutable objects? Is this depends on if i am ok with concurrent modifications during i work with the latest state i have saw and not need to update the state?

Upvotes: 2

Views: 742

Answers (5)

gnat
gnat

Reputation: 6219

Publication seems to be somewhat tricky concept, and the way it's explained in java concurrency in practice didn't work well to me (as opposed to many other multithreading concepts explained in this wonderful book).

With above in mind, let's first get clear on some simpler parts of your question.

  • when you state lets say thread1 finish first - how would you know that? or, to be more precise, how would thread2 "know" that? as far as I can tell this could be only possible with some sort of synchronization, explicit or not-so-explicit like in thread join (see the JLS - 17.4.5 Happens-before Order). Code you provided so far does not give sufficient details to tell whether this is the case or not

  • when you state that thread1 will update the cached state - how would thread2 "know" that? with the piece of code you provided, it looks entirely possible (but not guaranteed mind you) for thread2 to never know about this update

  • when you state thread2... will override the state what does override mean here? There's nothing in GlobalStateCache code example that could somehow guarantee that thread1 will ever notice this override. Even more, the code provided suggests nothing that would somehow impose happen-before relation of updates from different threads so one can even speculate that override may happen the other way around, you see?

  • the last but not the least, the wording the immutable state sounds rather fuzzy to me. I would say dangerously fuzzy given this tricky subject. The field state is mutable, it can be changed, well, by invoking method updateState right? From your code I would rather conclude that instances of MyImmutableState class are assumed to be immutable - at least that's what name tells me.

With all above said, what is guaranteed to be visible with the code you provided so far? Not much I'm afraid... but maybe better than nothing at all. The way I see it is...

For thread1, it is guaranteed that prior to invoking updateState it will see either null or properly constructed (valid) object updated from thread2. After the update, it is guaranteed to see either of properly constructed (valid) objects updated from thread1 or thread2. Note after this update thread1 is guaranteed not to see null per the very JLS 17.4.5 I refer to above ("...x and y are actions of the same thread and x comes before y in program order...")

For thread2, guarantees are pretty similar to above.

Essentially, all that is guaranteed with the code you provided is that both threads will see either null or one of properly constructed (valid) instances of MyImmutableState class.

Above guarantees may look insignificant at the first glance, but if you skim one page above the one with quote that confused you ("Immutable objects can be used safely etc..."), you'll find an example worth deeper drilling into in 3.5.1. Improper Publication: When Good Objects Go Bad.

Yeah object being immutable alone won't guarantee its visibility but it at least will guarantee that the object won't "explode from inside", like in example provided in 3.5.1:

public class Holder {
  private int n;

  public Holder(int n) { this.n = n; }

  public void assertSanity() {
    if (n != n)
      throw new AssertionError("This statement is false.");
  }
}

Goetz comments for above code begin at explaining issues true for both mutable and immutable objects,

...we say the Holder was not properly published. Two things can go wrong with improperly published objects. Other threads could see a stale value for the holder field, and thus see a null reference or other older value even though a value has been placed in holder...

...then he dives into what can happen if object is mutable,

...But far worse, other threads could see an up-todate value for the holder reference, but stale values for the state of the Holder. To make things even less predictable, a thread may see a stale value the first time it reads a field and then a more up-to-date value the next time, which is why assertSanity can throw AssertionError.

Above "AssertionHorror" may sound counter-intuitive but all the magic goes away if you consider scenario like below (completely legal per Java 5 memory model - and for a good reason btw):

  1. thread1 invokes sharedHolderReference = Holder(42);

  2. thread1 first fills n field with default value (0) then is going to assign it within constructor but...

  3. ...but scheduler switches to thread2,

  4. sharedHolderReference from thread1 becomes visible to thread2 because, say because why not? maybe optimizing hot-spot compiler decided it's a good time for that

  5. thread2 reads the up-todate sharedHolderReference with field value still being 0 btw

  6. thread2 invokes sharedHolderReference.assertSanity()

  7. thread2 reads the left side value of if statement within assertSanity which is, well, 0 then it is going to read the right side value but...

  8. ...but scheduler switches back to thread1,

  9. thread1 completes the constructor assignment suspended at step #2 above by setting n field value 42

  10. value 42 in the field n from thread1 becomes visible to thread2 because, say because why not? maybe optimizing hot-spot compiler decided it's a good time for that

  11. then, at some moment later, scheduler switches back to thread2

  12. thread2 proceeds from where it was suspended at step #6 above, ie it reads right-hand side of if statement, which is, well, 42 now

  13. oops our innocent if (n != n) suddenly turns into if (0 != 42) which...

  14. ...naturally throws AssertionError

As far as I understand, initialization safety for immutable objects just guarantees that above won't happen - no more... and no less

Upvotes: 4

CPerkins
CPerkins

Reputation: 9018

If I understand your question, immutability doesn't seem to be relevant here. You're just asking whether threads will see updates to shared objects.

[Edit] after an exchange of comments, I now see that you need also to hold a reference to your shared singleton state while doing some actions, and then setting the state to reflect that action.

The good news, as before, is that providing this will of necessity also solve your memory consistency issue.

Instead of defining separate synchronized getState and updateState methods, you'll have to perform all three actions without being interrupted: getState, yourAction, and updateState.

I can see three ways to do it:

1) Do all three steps inside a single synchronized method in GlobalStateCache. Define an atomic doActionAndUpdateState method in GlobalStateCache, synchronized of course on your state singleton, which would take a functor object to do your action.

2) Do getState and updateState as separate calls, and change updateState so that it checks to be sure state hasn't changed since the get. Define getState and checkAndUpdateState in GlobalStateCache. checkAndUpdateState will take the original state caller got from getState, and must be able to check if state has changed since your get. If it has changed, you'll need to do something to let caller know they potentially need to revert their action (depends on your use case).

3) Define a getStateWithLock method in GlobalStateCache. This implies that you'll also need to assure callers release their lock. I'd create an explicit releaseStateLock method, and have your updateState method call it.

Of these, I advise against #3, because it leaves you vulnerable to leaving that state locked in the event of some kinds of bugs. I'd also advise (though less strongly) against #2, because of the complexity it creates with what happens in the event that the state has changed: do you just abandon the action? Do you retry it? Must it be (can it be) reverted? I'm for #1: a single synchronized atomic method, which will look something like this:

public interface DimitarActionFunctor {
  public void performAction();
}
GlobalStateCache {    
  private MyImmutableState state = MyImmutableState.newInstance(null, null);     
    public MyImmutableState getState {     
      synchronized(state) {
        return state;    
      }
    }     
    public void doActionAndUpdateState(DimitarActionFunctor functor, State currentState, Args newArgs){       
      synchronized(state) {
         functor.performAction();
         state = MyImmutableState.newInstance(currentState, newArgs);    
      }
    } 
  } 
} 

Caller then constructs a functor for the action (an instance of DimitarActionFunctor), and calls doActionAndUpdateState. Of course, if the actions need data, you'll have to define your functor interface to take that data as arguments.

Again, I point you to this question, not for the actual difference, but for how they both work in terms of memory consistency: Difference between volatile and synchronized in Java

Upvotes: 1

Thresh
Thresh

Reputation: 943

Answering you "main" question: no Thread2 will not see the change. Immutable objects do not change :-)

So if Thread1 read state A and then Thread2 stores state B, Thread1 should read the variable again to see the changes.

Visibily of variables is affected by volatile keyword. If variable is declared as volatile then Java guarantees that if one thread updates the variable all other threads will see the change immediately (at the cost of speed).

Still immutable objects are very useful in multithreaded environments. I will give you an example how I used it once. Lets say you have an object that is periodically changed (life field in my case) by one thread and it is somehow processed by other threads (my program was sending it to clients over the network). These threads fail if the object is changed in the middle of processing (they send inconsistent life field state). If you make this object immutable and will create a new instance every time it changes, you don't have to write any synchronization at all. Updating thread will periodically publish new versions of an object and every time other threads read it they will have most recent version of it and can safely process it. This particular example saves time spent on synchronization but wastes more memory. This should give you a general understanding when you can use them.

Another link I found: http://download.oracle.com/javase/tutorial/essential/concurrency/immutable.html

Edit (answer comment):

I will explain my task. I had to write a network server that will send clients the most recent life field and will constantly update it. With design mentioned above, I have two types of threads:

  1. Thread that updates object that represents life field. It is immutable, so actually it creates a new instance every time and only changes reference. The fact that reference is declared volatile is crucial.
  2. Every client is served with its own thread. When client requests life field, it reads the reference once and starts sending. Because network connection can be slow, life field can be updated many times while this thread sends data. The fact that object is immutable guarantees that server will send consistent state of life field. In the comments you are concerned about changes made while this thread processes the object. Yes, when client receives data it may not be up to date but there is nothing you can do about it. This is not synchronization issue but rather a slow connection issue.

I am not stating that immutable objects can solve all of your concurrency problems. This is obviously not true and you point this out. I am trying to explain you where it actually can solve problems. I hope my example is clear now.

Upvotes: 0

Michael Entin
Michael Entin

Reputation: 7744

I think the key is to distinguish between objects and references.

The immutable objects are safe to publish, so any thread can publish object, and if any other thread reads a reference to such object - it can safely use the object. Of course, reader thread will see the immutable object state that was published at the moment the thread read the reference, it will not see any updates, until it reads the reference again.

It is very useful in many situations. E.g. if there is a single publisher, and many readers - and readers need to see a consistent state. The readers periodically read the reference, and work on the obtained state - it is guaranteed to be consistent, and it does not require any locking on reader thread. Also when it is OK to loose some updates, e.g. you don't care which thread updates the state.

Upvotes: 1

jefflub
jefflub

Reputation: 81

So much depends on the actual use case here that it's hard to make a recommendation, but it looks like you want some sort of Compare-And-Set semantics for the GlobalStateCache, using a java.util.concurrent.atomic.AtomicReference.

public class GlobalStateCache {
    AtomicReference<MyImmutableState> atomic = new AtomicReference<MyImmutableState>(MyImmutableState.newInstance(null, null);

    public State getState()
    {
        return atomic.get();
    }

    public void updateState( State currentState, Args newArgs )
    {
        State s = currentState;
        while ( !atomic.compareAndSet( s, MyImmutableState.newInstance( s, newArgs ) ) )
        {
            s = atomic.get();
        }
    }
}

This, of course, depends on the expense of potentially creating a few extra MyImmutableState objects, and whether you need to re-run myMethod(state) if the state has been updated underneath, but the concept should be correct.

Upvotes: 0

Related Questions