views:

259

answers:

3

I'm using JDK 1.4...so I don't have access to the nice concurrency stuff in 1.5+.

Consider the following class fragment:

private Map map = Collections.EMPTY_MAP;

public Map getMap() {
    return map;
}

public synchronized void updateMap(Object key, Object value) {
    Map newMap = new HashMap(map);
    newMap.put(key, value);
    map = Collections.unmodifiableMap(newMap);
}

Is it necessary to synchronize (or volatile) the map reference given that I will only be allowed to update the map via the updateMap method (which is synchronized)?

The map object will be accessed (read) in multiple threads, especially via Iterators. Knowing that Iterators will throw an exception if the back-end map's structure is changed, I figured I would make the map unmodifiable. So as I change the structure of the map via updateMap, the existing iterators will continue working on the "old" map (which is fine for my purposes).

The side effect is, I don't have to worry about synchronizing reads. In essense, I'm going to have a much greater magnitude of reads compared to writes. The threads that are currently iterating over the map object will continue to do so and any new threads that kick off will grab the latest map. (well, I assume it will considering erickson's comments here - http://stackoverflow.com/questions/300316/java-concurrency-scenario-do-i-need-synchronization-or-not)

Can somebody comment on whether or not this idea is a good one?

Thanks!

+6  A: 

You should use the volatile keyword, to ensure that Threads will see the most recent Map version. Otherwise, without synchronization, there is no guarantee that other threads will ever see anything except the empty map.

Since your updateMap() is synchronized, each access to it will see the latest value for map. Thus, you won't lose any updates. This is guaranteed. However, since your getMap() is not synchronized and map is not volatile, there is no guarantee that a thread will see the latest value for map unless that thread itself was the most recent thread to update the map. Use of volatile will fix this.

However, you do have access to the Java 1.5 and 1.6 concurrency additions. A backport exists. I highly recommend use of the backport, as it will allow easy migration to the JDK concurrency classes when you are able to migrate to a later JDK, and it allows higher performance than your method does. (Although if updates to your map are rare, your performance should be OK.)

Eddie
Yes, use volatile.
erickson
The volatile keyword was ignored by early JVMs. I'm not sure if that includes the 1.4 JVM. I know that 5 and 6 honour volatile, but not sure about 1.4
skaffman
+1 for the suggestion of using ConcurrentHashMap from the backport.
skaffman
This sounds right to me. To summarize: you don't need to synchronize the getMap() method __assuming__ the map variable is marked `volatile`
joshng
Volatile isn't guaranteed to work until 1.5, so you should use the backport.
Robin
My understanding is that volatile works as you'd expect ** for a single variable ** in JDK 1.4. However, in JDK 1.5, the JMM was strengthened so that a write to a volatile variable creates a "happens before" which guarantees memory synchronization of all writes that occurred before the volatile write. But it is for the reasons mentioned in comments above that I highly recommend use of the backport.
Eddie
You could just put synchronized before the getMap() method, depending on your performance profile targets.
kd304
What is the real consequence of not using volitale or the backport? Will the other threads *never* see changes after updateMap? These threads are short lived (typical in a Spring/Hibernate/Struts web app). If this class is wrapped in a Spring object, will that overcome any issues that arise from not using volitale?
T Reddy
Multi-threaded access to an object that is not thread safe can have unpredictable results. This is the consequence.
Robin
I understand the non-deterministic behavior in this space, but I think it's "ok" for my existing threads to work off of what is essentially a "cached" map...when the threads finish, I would imagine that at some point in the future, another thread starts up and gets the latest map...I just wonder, will it take milliseconds, or minutes for future threads to get the "latest" map? I guess that's why it's called non-deterministic... :)
T Reddy
@Robin: volatile in 1.4 is not as broken as you suggest. Nothing I have read suggests that volatile in 1.4 plain doesn't work for the variable defined to be volatile.
Eddie
@T Reddy: The likely consequence is that threads will see an old value for `map`. However, the JMM doesn't guarantee that threads will ever see any updates unless the variable is declared volatile or unless you synchronize. Why would you want to even take a chance of unpredictable behavior?
Eddie
A: 

Technically, if you use volatile map, you don't need to synchronize updateMap (updating 'map' is atomic, and all other instructions in that method operate on objects local to the current thread).

A: 

I know this isnt part of your question. But if you are worried about concurrency and are not using java 1.5 for ConcurrentHashMap. Use an immutable instance of a Hashtable. It is a blocking concurrent map implementation which handles all concurrency writes and reads.