1

I want to load some system information from mysql with java periodically, maybe 10 seconds once, and use one thread to finish this, at the same time, there are mutiple threads need to read this information, there are two situatons :

1, declare a public static map :

public  volatile static Map<String, String> info = new HashMap<String, String>();

// Context.java
// start a thread to load from mysql
private void load() {
    new Thread(new Runnable() {
        while(true) {
              // return a map
              info = loadFromDB();        // every time a new map 
              Thread.sleep(5 * 1000);     // sleep 5 seconds
           } 
     }).start();
}

// mutiple threads call this function
public String getConfig(String key) {
    return Context.map.get(key);
}

Just as the code shows, every time the thread load info from db, it's a new map, and I needn't change the map content, is this thread safe?

2, maybe I want a sorted map, and the read threads need to fetch the max key in the map, in this situation, should I use TreeMap or ConcurrentSkipListMap?

Update

change the Map info with volatile to avoid cache with cpu.

znlyj
  • 1,109
  • 3
  • 14
  • 34
  • Take a look here http://stackoverflow.com/questions/3222512/thread-safe-hash-map – AKS Mar 09 '14 at 08:46
  • there is a little difference with that situation, I think. – znlyj Mar 09 '14 at 08:55
  • Excerpt from the documentation of [HashMap](http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html) - `If multiple threads access a hash map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally`. Therefore, I would assume using `ConcurrentHashMap` is your best bet. – AKS Mar 09 '14 at 09:02
  • But there is no thread try to modify the map, it just change the reference, so I still confused. – znlyj Mar 09 '14 at 09:04
  • Absolutely! I agree with you completely considering that you are not updating the map loaded from the Database anywhere outside, and using it only for reading purposes. – AKS Mar 09 '14 at 09:11
  • So, I doubt wheather I need a concurrent map or not ? – znlyj Mar 09 '14 at 09:32
  • I think you should use `ConcurrentHashMap`. Consider a situation when one of the threads is trying to get a value using `Context.map.get(key)` while at the same time the reference of map is being changed to a new one using `info = loadFromDB();` !!! – AKS Mar 09 '14 at 09:56
  • So, that's why I post this question, since single field assignments in java are atomic, I think if the referece changed ,meanwhile a thread is trying to read some key, if will return null. – znlyj Mar 09 '14 at 10:03

3 Answers3

1

single field assignments in java are atomic, so if your thread builds a new hashmap inside the loadFromDB() method call (so it doesnt touch the existing info map) and then simply changes the value of the info reference to this new map it created the operation is ok - any other concurrent threads see either the old value of info or the new one - nothing in between.

a few points though:

  1. in this case you should make info a volatile field: public static volatile Map... to avoid situations where the value will be cached in processor cache and not updated when you change the reference.
  2. any code you write that reads values from the info map needs to get a reference to the map one, and only once, and use that reference for the operation. for example:

this is ok

Map infoMap = info;
value1 = infoMap.get("key1");
//do something
value2 = infoMap.get("key2");

but this is NOT ok

value1 = info.get("value1");
//do soemthing
value2 = info.get("value2");

because you might be reading value2 from a newer info map.

radai
  • 23,949
  • 10
  • 71
  • 115
  • since info map is always reference to the loaded informat map, why the code below can't work? – znlyj Mar 09 '14 at 08:54
  • @znlyj - because if there's a long delay between getting value1 and value2 then its possible the map was replaced and then the code got an old value of value1 with a new value of value2. thats generally a very bad idea. – radai Mar 09 '14 at 09:32
  • Since infoMap and info are both reference to the loaded map return by loadFromDB, why infoMap can avoid this situation? I think, the value of key2 alse maybe replaced by a new map. – znlyj Mar 09 '14 at 09:39
  • @znlyj - but in the 1st scenario i will always be getting stuff from the old map (since i kept a reference for it as infoMap) - the code gets a reference to the map, keeps it while its doing its thing, and does not see any newer maps (until the operation is complete and the next operation gets the current map again) – radai Mar 09 '14 at 11:19
  • Understand! The infoMap variable keep the reference to the old map, so that I can get the value hold by this old map, but info would be always reference to the newest map. – znlyj Mar 10 '14 at 01:40
1

For multi-threaded applications with shared memory, there is one important rule to remember. Regarding the Java Memory Model, the JLS7 states in 17.4.5:

[..] If all sequentially consistent executions are free of data races, [..] then all executions of the program will appear to be sequentially consistent.

At first glance, this sentence sounds weird. However, it exactly tells you what you have to know. In your case it means:

Without volatile

If you remove the qualifier volatile from the definition of the variable info, then there is a data race on this variable, because two threads can access the variable at the same time, and at least one operation is a write operation. That means, that sequential consistency is not guaranteed, and what that means is hard to explain. Most likely, you will see all kinds of weird exceptions, because the reader threads will see an inconsistent HashMap.

With volatile

If you add the qualifier volatile to the definition of the variable info, then there is no data race, neither on the variable info nor on any other memory location, because there is no memory location that can be accesses by two threads (with at least one write operation) at the same time. Note that operations on volatile variables are no data races.

That means that in all executions, all operations will appear to be execution in a total order that is consistent with program order. And that means, your application works as expected.

nosid
  • 48,932
  • 13
  • 112
  • 139
  • so, situation 1 would by work as expected, and how about situation 2, I want to get value from the sorted map. – znlyj Mar 09 '14 at 10:06
  • There is no need for a _concurrent_ data structure. Go with a `TreeMap`. – nosid Mar 09 '14 at 10:10
0

In theory yes, in practice no.

In theory you need to use a concurrent map as the implementation of get might not be thread-safe. A map implementation could use unsafe variables for internal optimization or re-hash on get calls or do other things that might cause certain threading issues to arise.

In practice as far as I can tell the get call is a simple array look-up and should be thread-safe. However this holds true only for the current Java version on a specific implementation (Oracle, Windows) and might be different somewhere else.

As the cost for using a concurrent over a non-concurrent map is very low, I would recommend to use a concurrent one even if just in doubt of whether or not it is thread-safe.

TwoThe
  • 13,879
  • 6
  • 30
  • 54
  • I don't modify the map, but create a new one to the reference, is this also not thread-safe? – znlyj Mar 09 '14 at 10:30
  • It is not about what you do with the map, it is about how `get()` is implemented by Java. It is not defined whether or not get is thread-safe, so you cannot just assume that it is and always will be. – TwoThe Mar 09 '14 at 10:35
  • Logic 101: If A implies B, then B doesn't imply A. Nothing specifies what causes a structural modification. However as I said: the his just a theoretical thing, in practice you can `get()` as much as you want, it won't fail. Just if you happen to design the controls for a nuclear reactor, it would be great if you don't count on it. – TwoThe Mar 09 '14 at 16:25
  • @TwoThe: You mean read operation with mutiple threads is not thread-safe? – znlyj Mar 10 '14 at 01:45
  • Please note me differentiating between theory and practice. My comment on theory is only as a statement on reading the JavaDocs properly: if those don't guarantee thread-safety, you shouldn't count on it. On a completely different topic: multiple reads is always thread-safe, you need writes to make it a mess. ;) – TwoThe Mar 10 '14 at 11:53