Memory visibility guarantees provided by inline locking in Java?

I need clarity as to what kind of memory visibility guarantees are provided with built-in locking in Java.

So, for example, let's say if I have a HashMap object that maps Strings to Person objects like this:

HashMap<String,Person> m = new HashMap<String, Person>();

      

And let's say we have a synchronized method inside one class:

public synchronized void addToMap(String name, Person p){
   m.put(name, p);
}

      

And also the same class has another synchronized method called get, for example:

public synchronized Person get(String name){
   return m.get(name);
}

      

I have two questions.

1

Now let's say that Thread A acquires the lock and issues the addToMap method, then exits the method and releases the lock.

And then thread B enters outside of the blocking method and changes the state of the Person object referenced by p.

Then, when Thread C acquires the same lock as Thread A and executes the get () method, is Thread C guaranteed to get the Person object in its last state? those. after Thread B changed it.

Now this example is very contrived and if I was using a multi-thread map then I know I should be using ConcurrentHashMap etc., this example is just being used to better explain my confusion about visibility guarantees.

I know that when a Thread acquires a lock, it is guaranteed to detect any changes that were made to the state of the object from the previous thread that held that lock, so I understand correctly that since Thread B's activity was executing without acquiring the lock, then is not Thread C guaranteed to see changes?

2

Now, if my guess for Question 1 is correct, then lets say that Thread B did indeed change the state of the Person object that reference p refers to within the synchronized method so that it acquired the lock, then is C guaranteed to see the change B made? My initial assumption here is that C is guaranteed to see the change that B made, but when I thought I was not sure if the actual change, to Person p, actually changes the state of the HashMap (which is part of the state of the locked object ) because it is not a structural modification such as adding or removing a display.

I know that internal locking provides visibility and atomicity guarantees, and volatile variables only guarantee visibility, but my confusion is about how these guarantees relate to actual objects (for example, actual objects in a mapping, for example the Person object referenced by p). not just object references.

Any help to clear this would be greatly appreciated.

+3


source to share


3 answers


I know that when a Thread acquires a lock, it is guaranteed to see any changes that were made to the state of an object from a previous thread that held that lock, so I understand correctly that since Thread B's activity was executed without acquiring the lock, it is not guaranteed what Thread C will see the change?

Yes. Basically, it doesn't matter how you got hold of the object (in this case, using the person reference stored on the map).

Whatever the thread changes, because it does not hold the lock, it is not part of the happen-before relationship, so subsequent reads do not guarantee that you will see the update.

Now, if my guess for Question 1 is correct, then let's say that Thread B actually changed the state of the Person object that reference p is referencing inside the synchronized method, so it acquired the lock, then is C guaranteed to see the change made by B?



If Thread B made a lock, and that lock was later acquired by Thread C, then Thread C will see the changes made by Thread B, regardless of whether there were changes inside the map, etc.

But it's important to remember that just because you are introducing locks doesn't mean you don't have data races. Without further synchronization, there is no guarantee that Thread B actually acquires the lock before Thread C.

I hope I understood your question correctly and I hope my answer was clear. Please let me know otherwise.

+4


source


1) True.

2) True, assuming Thread B:

  • changed p holding SAME lock, OR
  • the addToMap method is called after modifications, in which case it doesn't matter if Thread B made changes inside or outside the sync block.

However, there is another potential problem and that is the visibility of the mhashMap itself:



m = new HashMap<String, Person>();

      

This code is out of sync, which means that m will only be visible to the thread that created the object that contains m. Other threads may see a null or inconsistent state of the card. The solution is to declare m both mutable and final (which is why most immutable objects should declare their fields final, it's not just a convention for this).

A full description of the relationship between events and relationships can be found here .

+1


source


afaict, in the example you provided, the first lock is only on the card (i.e. it only affects the presence / absence of a given person on that card), and the second is on the Person instance. If any Person field changes when a Person is added to the list, the implicit synchronizations should not affect each other, so they will not change behavior or affect the prospects (accessors) of the Person instance in any way.

0


source







All Articles