Home > Software design >  Synchronizing Access to a Class Field
Synchronizing Access to a Class Field

Time:09-01

Assuming there is a field in a class that is changing/written infrequently but is being read from concurrently, is it necessary to protect it with a ReadWriteLock?

@Component
public class SomeClass {
    private Map<String, String> someField;

    @EventListener(ApplicationReadyEvent.class)
    @Scheduled(fixedRate = 15, timeUnit = TimeUnit.MINUTES)
    public void someScheduledActivity() {
        // replace the whole map (A)
        someField = theNewValue();
    }

    public String read(String id) {
        // reading from the map field (B)
        return this.someField.getOrDefault(id, null);
    }
}

As far as I understand, we must write lock at (A) and read lock at (B). But I have been told Java is different, and there is no need for this synchronisation (or any kind of synchronisation). This sounds very different and unexpected to me.

Are there any official Java documents/guidelines that could help clarify this?

CodePudding user response:

You don't need synchronization only if your operation is atomic (see https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html).

You should declare your variable volatile to assure you read the latest write.

CodePudding user response:

Incorrection may occur when the field is updated concurrently. But updating in someScheduledActivity method is serialized by default, so it's thread-safe and synchronization is not required. Declaring your variable volatile is a good idea, like @Edgar Domingues said.

CodePudding user response:

You don't have to use a ReadWriteLock but you do have to make sure updates to the field are visible to other threads.

You need to make the map field volatile to protect against memory consistency errors. It's not enough that it's atomic, putting volatile on it tells the JVM that this field is getting read by other threads so the write to it needs to made visible.

If your map doesn't change structurally then it's safe to access it without synchronization. That means no inserting or deleting entries. The warning about structural modifications is in the api doc: https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/HashMap.html

Visibility of the field holding the reference and visibility of the contents of that reference are two separate issues. A safely published map that isn't updated won't need to ensure memory visibility when it is getting read from. But it would be good to use an immutable map to enforce that.

The best reference for this is the book Java Concurrency in Praxtice.

TLDR: use volatile on the instance member declaration. Make sure map written to that field is immutable.

  • Related