This is a code snippet from collections's SynchronizedMap. My question is not specific to the code snippet below - but a generic one: Why does a get operation need synchronization?
public V get(Object key) {
synchronized (mutex) {return m.get(key);}
}
If your threads are only ever getting from the Map, the synchronization is not needed. In this case it might be a good idea to express this fact by using an immutable map, like the one from the Guava libraries, this protects you at compile time from accidentally modifying the map anyway.
The trouble begins when multiple threads are reading and modifying the map, because the internal structure of, e.g. the HashMap implementation from the Java standard libraries is not prepared for that. In this case you can either wrap an external serialization layer around that map, like
using the synchronized keyword,
slightly safer would be to use a SynchronizedMap, because then you can't forget the synchonized keyword everywhere it's needed,
protect the map using a ReadWriteLock, which would allow multiple concurrently reading threads (which is fine)
switch to an ConcurrentHashMap altogether, which is prepared for being accessed by multiple threads.
But coming back to you original question, why is the synchronization needed in the first place: This is a bit hard to tell without looking at the code of the class. Possibly it would break when the put or remove from one thread causes the bucket count to change, which would cause a reading thread to see too many / too few elements because the resize is not finished yet. Maybe something completely different, I don't know and it's not really important because the exact reason(s) why it is unsafe can change at any time with a new Java release. The important fact is only that it is not supported and your code will likely blow up one or another way at runtime.
If the table gets resized in the middle of the call to get(), it could potentially look in the wrong bucket and return null incorrectly.
Consider the steps that happen in m.get():
A hash is calculated for the key.
The current length of the table (the buckets in the HashMap) is read.
This length is used to calculate the correct bucket to get from the table.
The bucket is retrieved and the entries in the bucket are walked until a match is found or until the end of the bucket is reached.
If another thread changes the map and causes the table to be resized in between 2 & 3, the wrong bucket could be used to look for the entry, potentially giving an incorrect result.
The reason why synchronization is needed in a concurrent environment is, that java operations aren't atomic. This means that a single java operation like counter++ causes the underlaying VM to execute more than one machine operation.
Read value
Increment value
Write value
While those three operations are performed, another thread called T2 may be invoked and read the old value e.g 10 of that variable. T1 increments that value und writes the value 11 back. But T2 has read value 10! In cas that T2 should also increment this value, the result stays the same, namely 11 instead of 12.
The synchronisation will avoid such concurrent errors.
T1:
Set synchronizer token
Read value
Another thread T2 was invoked and tries to read the value. But since the synchronizer token was already set, T2 has to wait.
Increment value
Write value
Remove synchronizer token
T2:
Set synchronizer token
Read value
Increment value
Write value
Remove synchronizer token
By synchronising the get method you are forcing the thread to cross the memory barrier and read the value from the main memory. If you wouldn't synchronise the get method then the JVM takes liberties to apply underlying optimisations that might result in that thread reading blissfully unaware a stale value stored in registers and caches.
Related
Are actions in a thread prior to calling ConcurrentMap.remove() guaranteed to happen-before actions subsequent to seeing the removal from another thread?
Documentation says this regarding objects placed into the collection:
Actions in a thread prior to placing an object into any concurrent collection happen-before actions subsequent to the access or removal of that element from the collection in another thread.
Example code:
{
final ConcurrentMap map = new ConcurrentHashMap();
map.put(1, new Object());
final int[] value = { 0 };
new Thread(() -> {
value[0]++;
value[0]++;
value[0]++;
value[0]++;
value[0]++;
map.remove(1); // A
}).start();
new Thread(() -> {
if (map.get(1) == null) { // B
System.out.println(value[0]); // expect 5
}
}).start();
}
Is A in a happens-before relationship with B? Therefore, should the program only, if ever, print 5?
You have found an interesting subtle aspect of these concurrency tools that is easy to overlook.
First, it’s impossible to provide a general guaranty regarding removal and the retrieval of a null reference, as the latter only proves the absence of a mapping but not a previous removal, i.e. the thread could have read the map’s initial state, before the key ever had a mapping, which, of course, can’t establish a happens-before relationship with the actions that happened after the map’s construction.
Also, if there are multiple threads removing the same key, you can’t assume a happens-before relationship, when retrieving null, as you don’t know which removal has been completed. This issue is similar to the scenario when two threads insert the same value, but the latter can be fixed on the application side by only perform insertions of distinguishable values or by following the usual pattern of performing the desired modifications on the value object which is going to be inserted and to query the retrieved object only. For a removal, there is no such fix.
In your special case, there’s a happens-before relationship between the map.put(1, new Object()) action and the start of the second thread, so if the second thread encounters null when querying the key 1, it’s clear that it witnessed the sole removal of your code, still, the specification didn’t bother to provide an explicit guaranty for this special case.
Instead, the specification of Java 8’s ConcurrentHashMap says,
Retrievals reflect the results of the most recently completed update operations holding upon their onset. (More formally, an update operation for a given key bears a happens-before relation with any (non-null) retrieval for that key reporting the updated value.)
clearly ruling out null retrievals.
I think, with the current (Java 8) ConcurrentHashMap implementation, your code can’t break as it is rather conservative in that it performs all access to its internal backing array with volatile semantics. But that is only the current implementation and, as explained above, your code is a special case and likely to become broken with every change towards a real-life application.
No, you have the order wrong.
There is a happens-before edge from the put() to the subsequent get(). That edge is not symmetric, and doesn't work in the other direction. There is no happens-before edge from at get() to another get() or a remove(), or from a put() to another put().
In this case, you put an object in the map. Then you modify another object. That's a no-no. There's no edge from the those writes to the get() in the second thread, so those writes may not be visible to the second thread.
On Intel hardware, I think this will always work. However, it isn't guaranteed by the Java memory model, so you have to be wary if you ever port this code to different hardware.
A does not need to happen before B.
Only the original put happens before both. Thus a null at B means that A happened.
However write back of thread local memory cache and instruction order of ++ and remove are not mentioned. volatile is not used; instead a Map and an array are used to hopefully keep thread data synchrone. On writing the data back, in-order relation should hold again.
To my understanding A could remove and be written back, then the last ++ happen, and something like 4 being printed at B. I would add volatile to the array. The Map itself will go fine.
I am far from certain, but as I did not see a corresponding answer, I stick my neck out. (To learn myself.)
As ConcurrentHashMap is a thread safe collection, the statement map.remove(1) must have a read barrier and a write barrier if it alters the map. The expression map.get(1) must have a read barrier or one, or both of those operations are not thread safe.
In reality ConcurrentHashMap up to Java 7, uses partitioned locks, so it always has a read/write barrier for nearly every operation.
A ConcurrentSkipListMap doesn't have to use locks, but to perform any thread safe write action, a write barrier is required.
This means your test should always act as expected.
From the Source Code of ConcurrentHashMap
/**
171 * Number of unsynchronized retries in size and containsValue
172 * methods before resorting to locking. This is used to avoid
173 * unbounded retries if tables undergo continuous modification
174 * which would make it impossible to obtain an accurate result.
175 */
176 static final int RETRIES_BEFORE_LOCK = 2;
1.I have read iteration doesn't hold lock,so what does above statement means?
Do operations like get can also hold lock?Please provide scenario also.
2.Will the update operation running in thread 1 be visible to iteration in thread 2 if iteration over that element is yet to be made?(volatility and visibility?)
3.Are there any other situations besides updation where locking is done?
4.When getting data, a volatile read is used. If the volatile read results in a miss, then the lock for the segment is obtained for a last attempt at a successful read.What does this mean?What is volatile read?
I have read iteration doesn't hold lock,so what does above statement
means?
One can argue that the size method can go ahead and never hold a lock. But this implementation will get the size of the ConcurrentHashMap twice, if the size of first does not equal second it will retry. If the same is true it will lock all segments and get the size a last time.
Do operations like get can also hold lock? Please provide scenario
also.
Technically yes it can but probably will never happen. In the event a JVM publishes one of the CHM's entry entry value after the entry is made available the CHM will do the read under segment lock (again this probably will never happen).
Java 8 is releasing a new implementation of CHM so this is probably going to be outdated soon.
Will the update operation running in thread 1 be visible to iteration
in thread 2 if iteration over that element is yet to be
made?(volatility?)
If thread 1 issues the put prior to thread 2 issuing the get, then thread 2 will see the updated entry.
If, while thread 2 is doing a get at the same time thread 1 is doing the put then thread 2 may or may not see the entry (or it may, it's a matter of timing). This is because get is non-blocking. This is still thread safe because the CHM says that it will return the entry as it is at the moment you are searching the map.
Are there any other situations besides updation where locking is done?
Besides all modification methods, serialization requires a lock.
When getting data, a volatile read is used. If the volatile read
results in a miss, then the lock for the segment is obtained for a
last attempt at a successful read.What does this mean?What is volatile
read?
I have already eluded to this with my "technically" answer. What you are referring to is the readValueUnderLock method. According to the JMM the write of a non-final field, whether inline construction or within the constructor, inside an object can be published after the Object is available to another thread.
So
public Entry{
volatile Object value;
public Entry(Object v){
value = v;
}
}
Thread 1
Entry e = new Entry(new Object());
Thread 2
if(e != null)
Object value = e.value; // here, according to the JMM, value can be null.
// If value were final it would never be null
Reading the value under the lock synchronizes the read with the previous write from the other thread so that will always prevent a null value.
That all being said, this scenario is either highly unlikely or under x86 arch impossible.
this is a passage from JavaDoc regarding ConcurrentHashMap. It says retrieval operations generally do not block, so may overlap with update operations. Does this mean the get() method is not thread safe?
"However, even though all operations are thread-safe, retrieval
operations do not entail locking, and there is not any support for
locking the entire table in a way that prevents all access. This class
is fully interoperable with Hashtable in programs that rely on its
thread safety but not on its synchronization details.
Retrieval operations (including get) generally do not block, so may
overlap with update operations (including put and remove). Retrievals
reflect the results of the most recently completed update operations
holding upon their onset."
The get() method is thread-safe, and the other users gave you useful answers regarding this particular issue.
However, although ConcurrentHashMap is a thread-safe drop-in replacement for HashMap, it is important to realize that if you are doing multiple operations you may have to change your code significantly. For example, take this code:
if (!map.containsKey(key))
return map.put(key, value);
else
return map.get(key);
In a multi-thread environment, this is a race condition. You have to use the ConcurrentHashMap.putIfAbsent(K key, V value) and pay attention to the return value, which tells you if the put operation was successful or not. Read the docs for more details.
Answering to a comment that asks for clarification on why this is a race condition.
Imagine there are two threads A, B that are going to put two different values in the map, v1 and v2 respectively, having the same key. The key is initially not present in the map. They interleave in this way:
Thread A calls containsKey and finds out that the key is not present, but is immediately suspended.
Thread B calls containsKey and finds out that the key is not present, and has the time to insert its value v2.
Thread A resumes and inserts v1, "peacefully" overwriting (since put is threadsafe) the value inserted by thread B.
Now thread B "thinks" it has successfully inserted its very own value v2, but the map contains v1. This is really a disaster because thread B may call v2.updateSomething() and will "think" that the consumers of the map (e.g. other threads) have access to that object and will see that maybe important update ("like: this visitor IP address is trying to perform a DOS, refuse all the requests from now on"). Instead, the object will be soon garbage collected and lost.
It is thread-safe. However, the way it is being thread-safe may not be what you expect. There are some "hints" you can see from:
This class is fully interoperable with Hashtable in programs that
rely on its thread safety but not on its synchronization details
To know the whole story in a more complete picture, you need to be aware of the ConcurrentMap interface.
The original Map provides some very basic read/update methods. Even I was able to make a thread-safe implementation of Map; there are lots of cases that people cannot use my Map without considering my synchronization mechanism. This is a typical example:
if (!threadSafeMap.containsKey(key)) {
threadSafeMap.put(key, value);
}
This piece of code is not thread-safe, even though the map itself is. Two threads calling containsKey() at the same time could think there is no such key they both therefore insert into the Map.
In order to fix the problem, we need to do extra synchronization explicitly. Assume the thread-safety of my Map is achieved by synchronized keywords, you will need to do:
synchronized(threadSafeMap) {
if (!threadSafeMap.containsKey(key)) {
threadSafeMap.put(key, value);
}
}
Such extra code needs you to know about the "synchronization details" of the map. In the above example, we need to know that the synchronization is achieved by "synchronized".
ConcurrentMap interface take this one step further. It defines some common "complex" actions that involves multiple access to map. For example, the above example is exposed as putIfAbsent(). With these "complex" actions, users of ConcurrentMap (in most case) don't need to synchronise actions with multiple access to the map. Hence, the implementation of Map can perform more complicated synchronization mechanism for better performance. ConcurrentHashhMap is a good example. Thread-safety is in fact maintained by keeping separate locks for different partitions of the map. It is thread-safe because concurrent access to the map will not corrupt the internal data structure, or cause any update lost unexpected, etc.
With all the above in mind, the meaning of Javadoc will be clearer:
"Retrieval operations (including get) generally do not block" because ConcurrentHashMap is not using "synchronized" for its thread-safety. The logic of get itself takes care of the thread-safeness; and If you look further in the Javadoc:
The table is internally partitioned to try to permit the indicated number
of concurrent updates without contention
Not only is retrieval non-blocking, even updates can happen concurrently. However, non-blocking/concurrent-updates does not means that it is thread-UNsafe. It simply means that it is using some ways other than simple "synchronized" for thread-safety.
However, as the internal synchronization mechanism is not exposed, if you want to do some complicated actions other than those provided by ConcurrentMap, you may need to consider changing your logic, or consider not using ConcurrentHashMap. For example:
// only remove if both key1 and key2 exists
if (map.containsKey(key1) && map.containsKey(key2)) {
map.remove(key1);
map.remove(key2);
}
ConcurrentHashmap.get() is thread-safe, in the sense that
It will not throw any exception, including ConcurrentModificationException
It will return a result that was true at some (recent) time in past. This means that two back-to-back calls to get can return different results. Of course, this true of any other Map as well.
HashMap is divided into "buckets" based on hashCode. ConcurrentHashMap uses this fact. Its synchronization mechanism is based on blocking buckets rather than on entire Map. This way few threads can simultaneously write to few different buckets (one thread can write to one bucket at a time).
Reading from ConcurrentHashMap almost doesn't use synchronization. Synchronization is used when while fetching value for key, it sees null value. Since ConcurrentHashMap can't store null as values (yes, aside from keys, values also can't be nulls) it suggests that fetching null while reading happened in the middle of initializing map entry (key-value pair) by another thread: when key was assigned, but value not yet, and it still holds default null.
In such case reading thread will need to wait until entry will be written fully.
So results from read() will be based on current state of map. If you read value of key that was in the middle of updating you will likely get old value since writing process hasn't finished yet.
get() in ConcurrentHashMap is thread-safe because It reads the value
which is Volatile. And in cases when value is null of any key, then
get() method waits till it gets the lock and then it reads the updated
value.
When put() method is updating CHM, then it sets the value of that key to null, and then it creates a new entry and updates the CHM. This null value is used by get() method as signal that another thread is updating the CHM with the same key.
It just means that when one thread is updating and one thread is reading there is no guarantee that the one that called the ConcurrentHashMap method first, in time, will have their operation occur first.
Think about an update on the item telling where Bob is. If one thread asks where Bob is at about the same time that another thread updates to say he came 'inside', you can't predict whether the reader thread will get Bob's status as 'inside' or 'outside'. Even if the update thread calls the method first, the reader thread might get the 'outside' status.
The threads will not cause each other problems. The code is ThreadSafe.
One thread won't go into an infinite loop or start generating wierd NullPointerExceptions or get "itside" with half of the old status and half of the new.
This seems wrong.
static ConcurrentHashMap k; //multiple threads have access to k
X o = k.get("LL");
o.a = 6;
If multiple threads access k concurrently, and get k("LL"), then update (o.a = #) without k.put("ll",o), without synchronizing on 'o', or on 'k' what happens?
A ConcurrentMap has conditional operations that guarantee atomic insert/removal and replacement of key/value pairs. Additionally, accessing a ConcurrentMap creates a happens-before relationship so you can make certain guarantees about the ordering of your code.
In the code presented, the line:
X o = k.get("LL");
accesses the current X value for the key "LL". The next line modifies the a property. Without knowing the implementation of X, this is Java so we know that there is no method call here. If (and only if) the a property is marked as volatile then some subsequent code accessing the X at "LL" will see the a value as 6. If it isn't volatile then there are no guarantees at all. They will probably see 6, particularly on an SMP x86 box, with not many threads doing much at the time. In production, on a big NUMA box, they are less likely to. Mutability brings with it all sorts of complications and difficulty.
Generally, you'll find it is easier to reason about the state the map is in if you use immutable keys AND values.
The ConcurrentHashMap guarantees that getting a value is atomic, but it can't control what you do with the values you get from it. Modifying values in the hashmap is fine from the ConcurrentHashMap's view but may still not result in the behaviour you want. To be sure about thread-safety you need to consider exactly what each thread that has access to it does.
Putting the value back in the ConcurrentHashMap seems redundant and doesn't make the whole operation any safer. You're already modifying the object outside of any synchronization.
Additional synchronization may be necessary, but I can't tell for sure without seeing more context.
Simply speaking:
o.a=6
is an atomic operation, all the threads will compete, and the last thread setting it will "win", overwriting the value.
More specifically, the ConcurrentHashMap only guarantees that the link between a key and its associated value is handled taking multiple threads into account - ie put and get are atomic.
This does not prevent any thread from modifying attributes of the value once it gets a reference to it!
I'm study java.util.concurrent library and find many infinite loops in the source code, like this one
//java.util.concurrent.atomic.AtomicInteger by Doug Lea
public final int getAndSet(int newValue) {
for (;;) {
int current = get();
if (compareAndSet(current, newValue))
return current;
}
}
I wonder, in what cases actual value can not be equal to the expected value (in this case compareAndSet returns false)?
Many modern CPUs have compareAndSet() map to an atomic hardware operation. That means, it is threadsafe without requiring synchronization (which is a relatively expensive operation in comparison). However, it's only compareAndSet() itself with is atomic, so in order to getAndSet() (i.e. set the variable to a given value and return the value it had at that time, without the possibility of it being set to a different value in between) the code uses a trick: first it gets the value, then it attempts compareAndSet() with the value it just got and the new value. If that fails, the variable was manipulated by another thread inbetween, and the code tries again.
This is faster than using synchronization if compareAndSet() fails rarely, i.e. if there are not too many threads writing to the variable at the same time. In an extreme case where many threads are writing to the variable at all times, synchronization can actually be faster because while there is an overhead to synchronize, other threads trying to access the variable will wait and be woken up when it's their turn, rather than having to retry the operation repeatedly.
When the value is modified in another thread, the get() and compareAndSet() can see different values. This is the sort of thing a concurrent library needs to worry about.
This is not an infinite loop, it is good practice when dealing with a TAS (test and set) algorithm. What the loop does is (a) read from memory (should be volatile semantics) (b) compute a new value (c) write the new value if the old value has not in the meantime changed.
In database land this is known as optimistic locking. It leverages the fact that most concurrent updates to shared memory are uncontended, and in that case, this is the cheapest possible way to do it.
In fact, this is basically what an unbiased Lock will do in the uncontended case. It will read the value of the lock, and if it is unlocked, it will do a CAS of the thread ID and if that succeeds, the lock is now held. If it fails, someone else got the lock first. Locks though deal with the failure case in a much more sophisticated way than merely retrying the op over and over again. They'll keep reading it for a little while incase the lock is quickly unlocked (spin-locking) then usually go to sleep for bit to let other threads in until their turn (exponential back-off).
Here is an actual usage of the compareAndSet operation: Imagine that you design an algorithm that calculates something in multiple threads.
Each thread remembers an old value and based on it performs a complicated calculation.
Then it wants to set the new result ONLY if the old value hasn't been already changed by another calculation thread. If the old value is not the expected one the thread discards its own work, takes a new value and restarts the calculations. It uses compareAndSet for that.
Further other threads are guaranteed to get only fresh values to continue the calculations.
The "infinite" loops are used to implement "busy waiting" which might be much less expensive than putting the thread to sleep especially when the thread contention is low.
Cheers!