This is the problem: we want a hash table whose entries are thread-safe.
Suppose I have a hash table of <String, Long>, and I want to increase the value of one of the entries thread safely: is the following OK?:
HashMap<String , Long> hashTable = new HashMap<String, Long>();
Then whenever I want to increase an entry:
Synchronized (hashTable.get("key"))
{
Long value = hashTable.get("key");
value++;
hashTable.put("key", value);
}
I think it is better than ConcurrentHashMap, as it locks just one entry, unlike ConcurrentHashMap which uses buckets, and lock a group of entries together.
More importantly, I don't know how to increment it using COncurrenHashMap safely. For example I think the following code is not correct:
ConcurrentHashMap<String , Long> hashTable = new ConcurrentHashMap<String, Long>();
Long value = hashTable.get("key");
value++;
hashTable.put("key", value);
I think it is not correct, because two threads can read the key one after another, and write one after another and end up in a wrong value.
What do you think guys?
Your proposed approach is not thread-safe because the initial hashTable.get() operation -- by which you obtain the object on which you intend to synchronize -- is not itself synchronized relative to other threads put()ing a value associated with the same key. Moreover, your code does not account for the possibility of new values being added to the map or keys being removed from the map (so-called "structural modifications"). If ever that can happen, regardless of key, then those actions have to be synchronized with respect to all other accesses to the map.
You are right, however, that ConcurrentHashMap does not solve these problems either. It is thread-safe with respect to the individual operations it provides, which include some that Map itself does not define, but series of operations that must be performed as an uninterrupted unit still need to be protected by synchronization.
I suggest a slightly different approach: use a ConcurrentHashMap with AtomicLong, which is mutable, as your value type instead of Long:
ConcurrentHashMap<String, AtomicLong> map;
Then, to update the value for a key, even if you're not confident that the key already has an entry in the map, you do this:
AtomicLong value = map.putIfAbsent(key, new AtomicLong(0));
long updatedValue = value.incrementAndGet();
The putIfAbsent() ensures that value objects are not clobbered by conflicting put operations. The use of AtomicLong avoids the need for multiple operations to be jointly synchronized, because only one map access is needed -- the value retrieved is shared by all threads accessing it, and can itself be atomically updated without further accessing the map.
If you can be certain that the map already has a mapping for the given key, then you can simply do this:
AtomicLong value = map.get(key);
long updatedValue = value.incrementAndGet();
One way or the other, I think this is about the best you can do for the operations you describe and imply.
Update:
You could even consider combining the two approaches like this:
AtomicLong value = map.get(key);
if (value == null) {
value = map.putIfAbsent(key, new AtomicLong(0));
}
long updatedValue = value.incrementAndGet();
That supposes that it will be comparatively rare that there is not already a mapping for the given key, and it avoids creating a new AtomicLong in that case. If no mapping is found then the map must be accessed a second time to ensure that there is a mapping and to get the corresponding value, but here we still need putIfAbsent() if we want to avoid synchronization, because it is possible for two threads to both try to add a mapping for the same key, at about the same time. That's more costly when a new entry needs to be added, but it's possible that it would turn out to be less costly on average than my first suggestion. As with any performance question, however, it is essential to test.
Related
Let's say I have a Java Hashmap where the keys are strings or whatever, and the values are lists of other values, for example
Map<String,List<String>> myMap=new HashMap<String,List<String>>();
//adding value to it would look like this
myMap.put("catKey", new ArrayList<String>(){{add("catValue1");}} );
If we have many threads adding and removing values from the lists (not changing the keys just the values of the Hashmap) is there a way to make the access to the lists only threadsafe? so that many threads can edit many values in the same time?
Use a synchronized or concurrent list implementation instead of ArrayList, e.g.
new Vector() (synchronized)
Collections.synchronizedList(new ArrayList<>()) (synchronized wrapper)
new CopyOnWriteArrayList<>() (concurrent)
new ConcurrentLinkedDeque<>() (concurrent, not a List)
The last is not a list, but is useful if you don't actually need access-by-index (i.e. random access), because it performs better than the others.
Note, since you likely need concurrent insertion of the initial empty list into the map for a new key, you should use a ConcurrentHashMap for the Map itself, instead of a plain HashMap.
Recommendation
Map<String, Deque<String>> myMap = new ConcurrentHashMap<>();
// Add new key/value pair
String key = "catKey";
String value = "catValue1";
myMap.computeIfAbsent(key, k -> new ConcurrentLinkedDeque<>()).add(value);
The above code is fully thread-safe when adding a new key to the map, and fully thread-safe when adding a new value to a list. The code doesn't spend time obtaining synchronization locks, and don't suffer the degradation that CopyOnWriteArrayList has when the list grows large.
The only problem is that it uses a Deque, not a List, but the reality is that most uses of List could as easily be using a Deque, but is specifying a List out of habit, so this is likely an acceptable change.
There is a ConcurrentHashMap class which implements ConcurrentMap which can be used for thread-safe Map handling. compute, putIfAbsent, merge, all thread-safely handle multiple things trying to affect the same value at once.
Firstly use the concurrent hash map which will synchronize that particular bucket.
Secondly atomic functions must be used, otherwise when one thread will use the get method another thread can call the put method. Like below
// wrong
if(myMap.get("catKey") == null){
myMap.put("catKey",new ArrayList<String>(){{add("catValue1");}});
}
//correct
myMap.compute("catKey", (key, value) -> if(value==null){return new ArrayList<String>(){{add("catValue1");}}} return value;);
I have a case of nested maps as follows:
private final static Map<String, TreeMap<Long,String>> outerConcurrentMap = new ConcurrentHashMap<>();
I know that ConcurrentHashMap is thread safe, but I want to know about the TreeMaps this CHM holding, are they also thread safe inside CHM ?
The operations I am doing are:
If specific key is not found --> create new TreeMap and put against key.
If key is found then get the TreeMap, and update it.
Retrieve TreeMap from CHM using get(K).
Retreive data from TreeMap using tailMap(K,boolean) method.
clear() the CHM.
I want a thread-safe structure in this scenario. Is the above implementation thread-safe or not? If not then please suggest a solution.
Once you've done TreeMap<?, ?> tm = chm.get(key); you are not in thread safe territory any longer. In particular, if another thread updates the treemap (through the CHM or not) you may or may not see the change. Worse, the copy of the map that you have in tm may be corrupted...
One option would be to use a thread safe map, such as a ConcurrentSkipListMap.
Simple answer: no.
If your map is a ConcurrentHashMap, then all operations that affect the state of your hashmap are thread-safe. That does not at all mean that objects stored in that map become thread-safe.
How would that work; you create any kind of object, and by adding it to such a map, the object itself becomes thread-safe? And when you remove that object from the map, the "thread-unsafety" is restored?!
Assuming you're doing all of this in multiple threads, no, it's not thread-safe.
Ignore the fact that you've accessed the TreeMap via a ConcurrentHashMap - you end up with multiple threads accessing the TreeMap at the same time, including one or more of them writing to the map. That's not safe, because TreeMap isn't thread-safe for that situation:
Note that this implementation is not synchronized. If multiple threads access a map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally.
Some your scenarios are thread-safe, some are not:
1. Yes this is thread safe though other threads cannot see newly created TreeMap until you put it to CHM. But this should be implemented carefully to avoid race conditions - you should make it sure that checking and insertion are performed atomically:
// create an empty treemap somewhere before
TreeMap<Long, String> emptyMap = new TreeMap<>();
...
// On access, use putIfAbsent method to make sure that if 2 threads
// try to get same key without associated value sumultaneously,
// the same empty map is returned
if (outerConcurrentMap.putIfAbsent(key, emptyMap) == null) {
emptyMap = new TreeMap<>();
};
map = outerConcurrentMap.get(key);
2, 3, 4. No, you first need to lock this TreeMap by explicit lock or using synchronized. TreeMap is not synchronized by itself.
5. Yes, this is operation is performed on CHM, so it is thread-safe.
If you need fully thread-safe sorted map, use ConcurrentSkipListMap instead. It is slower than TreeMap but its internal structure doesn't need to lock full collection during access thus making it effective in concurrent environment.
The TreeMap itself should not be thread safe. Since only the methods of the ConcurrentHashMap are effected.
What you could do is following:
private final static Map<String, SortedMap <Long,String>> outerConcurrentMap= new ConcurrentHashMap<String, SortedMap <Long,String> >();
static {
// Just an example
SortedMap map = Collections.synchronizedSortedMap(new TreeMap(...));
outerConcurrentMap.put("...",map);
}
I came across this dilemma at work and wanted to see if there is a better solution... it feels like there should be an easier, cleaner answer.
Goal: Concurrently access a map with locks at the key level, not at the entire map level, to ensure atomicity while impacting performance as little as possible.
I have a Map which needs to be concurrent. *(Added) The map will be filled with an unknown amount of entries over time. I have multiple readers and a single writer. The writer does a "check-then-put" and the reader does a simple get(). I need these to be atomic... but only at the key level. So for example, if the reader is checking for Key X, and the writer is writing to Key Y, I don't care if I miss the write to Key Y. If the reader/writer is working on the same key however I need that to be atomic.
The easiest solution is to lock the whole map. But this seems like it would impact performance, since there are about 10,000 keys that will end up in the map. (If that doesn't seem like it would hurt performance because the size of the Map is relatively small, let's pretend the Map has many more keys, for arguments sake.)
As far as I know, ConcurrentHashMap will not guarantee the "per-key" atomic behavior I need.
The next solution that came to mind was to have an array of lock objects. You would index into that array of lock Object()'s based on a hash of the original key. This would still have some contention since you have less locks than you have keys into the original map. I'm aware that ConcurrentHashMap does a similar thing under the hood (striping) to provide concurrency (but not atomicity).
Is there an easier way to perform this type of per-key or striped locking?
Thanks.
This concern can come up when value generation is a time-consuming process. You don't want to lock the whole map and find a missing value, and keep the map locked while you generate the value. You could release the map during generation, but then you could have two simultaneous misses and generations.
Instead of directly storing the value with the key, store it inside a reference object:
public class Ref<T>
{
private T value;
public T getValue()
{
return value;
}
public void setValue(T value)
{
this.value = value;
}
}
So if you originally had a map of Map<String, MyThing>, you instead use Map<String, Ref<MyThing>>. Don't bother with a concurrent implementation, just use HashMap or LinkedHashMap or whatever.
Now you can lock the map to find or create a reference holder, and then release the map. Following that, you can lock the reference to find or create the value object:
String key; // key you're looking up
Map<String, Ref<MyThing>> map; // the map
// Find the reference container, create it if necessary
Ref<MyThing> ref;
synchronized(map)
{
ref = map.get(key);
if (ref == null)
{
ref = new Ref<MyThing>();
map.put(key, ref);
}
}
// Map is released at this point
// Now get the value, creating if necessary
MyThing result;
synchronized(ref)
{
result = ref.getValue();
if (result == null)
{
result = generateMyThing();
ref.setValue(result);
}
}
// result == your existing or new object
I have a map. Lets say:
Map<String, Object> map = new HashMap<String, Object>();
Multiple threads are accessing this map, however each thread accesses only its own entries in the map. This means that if thread T1 inserts object A into the map, it is guaranteed that no other thread will access object A. Finally thread T1 will also remove object A.
It is guaranteed as well that no thread will iterate over the map.
Does this map need to be synchronized? If yes how would you synchronize it? (ConcurrentHashMap, Collections.synchronizedMap() or synchronized block)
Yes, you would need synchronization, or a concurrent map. Just think about the size of the map: two threads could add an element in parallel, and both increment the size. If you don't synchronize the map, you could have a race condition and it would result in an incorrect size. There are many other things that could go wrong.
But you could also use a different map for each thread, couldn't you?
A ConcurrentHashMap is typically faster that a synchronized HashMap. But the choice depends on your requirements.
If you're sure that there's only one entry per thread and none thread iterates/searches through the map, then why do you need a map?
You can use ThreadLocal object instead which will contain thread-specific data. If you need to keep string-object pairs, you can create an special class for this pair, and keep it inside ThreadLocal field.
class Foo {
String key;
Object value;
....
}
//below was your Map declaration
//Map<String, Object> map = ...
//Use here ThreadLocal instead
final ThreadLocal<Foo> threadLocalFoo = new ThreadLocal<Foo>();
...
threadLocalFoo.set(new Foo(...));
threadLocalFoo.get() //returns your object
threadLocalFoo.remove() //clears threadLocal container
More info on ThreadLocals you can find in ThreadLocal javadocs.
I would say that yes. Getting the data is not the issue, adding the data is.
The HashMap has a series of buckets (lists); when you put data to the HashMap, the hashCode is used to decide in which bucket the item goes, and the item is added to the list.
So it can be that two items are added to the same bucket at the same time and, due to some run condition, only one of them is effectively stored.
You have to synchronize writing operations in the map. If after initializating the map, no thread is going to insert new entries, or delete entries in the map you don't need to synchronize it.
However, in your case (where each thread has its own entry) I'd recommend using ThreadLocal, which allows you to have a "local" object which will have different values per thread.
Hope it helps
For this scenario I think ConcurrentHashMap is the best Map, because both Collections.synchronizedMap() or synchronized block (which are basically the same) have more overhead.
If you want to insert entries and not only read them in different threads you have to synchronize them because of the way the HashMap works.
- First of all its always a practice to write a Thread-safe code, specially in cases like the above, not in all conditions.
- Well its better to use HashTable which is a synchronized Map, or java.util.concurrent.ConcurrentHashMap<K,V>.
Preface: I'm know that in most cases using a volatile field won't yield any measurable performance penalty, but this question is more theoretical and targeted towards a design with an extremly high corrency support.
I've got a field that is a List<Something> which is filled after constrution. To save some performance I would like to convert the List into a read only Map. Doing so at any point requires at least a volatile Map field so make changes visible for all threads.
I was thinking of doing the following:
Map map;
public void get(Object key){
if(map==null){
Map temp = new Map();
for(Object value : super.getList()){
temp.put(value.getKey(),value);
}
map = temp;
}
return map.get(key);
}
This could cause multiple threads to generate the map even if they enter the get block in a serialized way. This would be no big issue, if threads work on different identical instances of the map. What worries me more is:
Is it possible that one thread assigns the new temp map to the map field, and then a second thread sees that map!=null and therefore accesses the map field without generating a new one, but to my suprise finds that the map is empty, because the put operations where not yet pushed to some shared memory area?
Answers to comments:
The threads only modify the temporary map after that it is read only.
I must convert a List to a Map because of some speical JAXB setup which doesn't make it feasable to have a Map to begin with.
Is it possible that one thread assigns the new temp map to the map field, and then a second thread sees that map!=null and therefore accesses the map field without generating a new one, but to my suprise finds that the map is empty, because the put operations where not yet pushed to some shared memory area?
Yes, this is absolutely possible; for example, an optimizing compiler could actually completely get rid of the local temp variable, and just use the map field the whole time, provided it restored map to null in the case of an exception.
Similarly, a thread could also see a non-null, non-empty map that is nonetheless not fully populated. And unless your Map class is carefully designed to allow simultaneous reads and writes (or uses synchronized to avoid the issue), you could also get bizarre behavior if one thread is calling its get method while another is calling its put.
Can you create your Map in the ctor and declare it final? Provided you don't leak the map so others can modify it, that should suffice to make your get() safely sharable by multiple threads.
When you really in doubt whether an other thread could read an "half completed" map
(I don't think so, but never say never ;-), you may try this.
map is null or complete
static class MyMap extends HashMap {
MyMap (List pList) {
for(Object value : pList){
put(value.getKey(), value);
}
}
}
MyMap map;
public Object get(Object key){
if(map==null){
map = new MyMap (super.getList());
}
return map.get(key);
}
Or does someone see a new introduced problem ?
In addition to the visibility concerns previously mentioned, there is another problem with the original code, viz. it can throw a NullPointerException here:
return this.map.get(key)
Which is counter-intuitive, but that is what you can expect from incorrectly synchronized code.
Sample code to prevent this:
Map temp;
if ((temp = this.map) == null)
{
temp = new ImmutableMap(getList());
this.map = temp;
}
return temp.get(key);