Java ConcurrentHashMap actions atomicity - java

This may be a duplicate question, but I've found this part of code in a book about concurrency. This is said to be thread-safe:
ConcurrentHashMap<String, Integer> counts = new ...;
private void countThing(String thing) {
while (true) {
Integer currentCount = counts.get(thing);
if (currentCount == null) {
if (counts.putIfAbsent(thing, 1) == null)
break;
} else if (counts.replace(thing, currentCount, currentCount + 1)) {
break;
}
}
}
From my (concurrency beginners') point of view, thread t1 and thread t2 could both read currentCount = 1. Then both threads could change the maps' value to 2. Can someone please explain me if the code is okay or not?

The trick is that replace(K key, V oldValue, V newValue) provides the atomicity for you. From the docs (emphasis mine):
Replaces the entry for a key only if currently mapped to a given value. ... the action is performed atomically.
The key word is "atomically." Within replace, the "check if the old value is what we expect, and only if it is, replace it" happens as a single chunk of work, with no other threads able to interleave with it. It's up to the implementation to do whatever synchronization it needs to make sure that it provides this atomicity.
So, it can't be that both threads see currentAction == 1 from within the replace function. One of them will see it as 1, and thus its invocation to replace will return true. The other will see it as 2 (because of the first call), and thus return false — and loop back to try again, this time with the new value of currentAction == 2.
Of course, it could be that a third thread has updated currentAction to 3 in the meanwhile, in which case that second thread will just keep trying until it's lucky enough to not have anyone jump ahead of it.

Can someone please explain me if the code is okay or not?
In addition to yshavit's answer, you can avoid writing your own loop by using compute which was added in Java 8.
ConcurrentMap<String, Integer> counts = new ...;
private void countThing(String thing) {
counts.compute(thing, (k, prev) -> prev == null ? 1 : 1 + prev);
}

With put you can too replace the value.
if (currentCount == null) {
counts.put(thing, 2);
}

Related

Doubly Check Lock Dosen't work on this java code?

I am trying to fix a service by optimising the synchronisation blocks. I am getting two different values and my doubly check singleton with a volatile string dosen't seem to work?
The get and Increment string puts a row lock on DB and increments the string so the unique update is taken care at DB level only. So in the first case, no issues.
Problem lies in the else blozk. When correlation ID is not null, then we try and fetch an already mapped value, if this is the first call. then we first map the value and then return it. This mapping has to be synchronised so that two different threads don't update next val agter both of them find it to be null.
This class is also a singleton service.
public class RangeQueryService{
private volatile String nextValue=null;
public String getNextIncrement(String name, String correlationId) throws SomeCheckedException {
try {
if (correlationId == null) {
nextValue = rangeFetch.getAndIncrementAsString(name);
} else { //Enter Sync branch
// mapper Will Return null if no value is mapped.
nextValue = mapper.mapToB(SOME_CONST, correlationId);
// Avoid syncronization overhead if value is already fetched. Only enter if nextVal is null.
if (nextValue == null) {
synchronized (this) {
Doubly Check lock pattern, as two threads can find null simultaneously, and wait on the critical section.
if(nextValue==null){
nextValue = rangeFetch.getAndIncrementAsString(name);
idMapper.mapToB(SOME_CONST, correlationId, nextValue, DURATION);
}
}
}
}
return nextValue;
} catch (Exception e) {
throw new SomeCheckedException("Error!" + e.getMessage());
}
}
It Return both 19 and 20. it should only return 19.
Output:
headerAfterProcessOne: 0000000019, headerAfterProcessTwo: 0000000020
If I understood you in the right way, you expect one thread (lets call it A) to wait for another (which increments the value to 19, B), and then skip the incrementing because nextValue is 19 and not null. But the change is unseen by the waiting thread.
The possible scenario is much more complicated, as I see:
The problem is that 19 returned by the A thread, the one which waits, as it immediately skips the whole block after the volatile value is posted by the B thread at line:
nextValue = rangeFetch.getAndIncrementAsString(name);
Another case, the A thread enters the method and nextValue is already posted.
So it immediately jumps to return statement and return 19, which was set by the B thread (Yes, it is unexpected, but yet this happens sometimes). You should not expect the A thread to wait for the B to finish executing. The B (which reached synchronized block first) finish processing (incrementing) the value and returns 20.
There are possible workarounds for that, though:
if (nextValue == null) {
synchronized(this) {
if(nextValue == null) {
String localTemp = rangeFetch.getAndIncrementAsString(name);
idMapper.mapToB(SOME_CONST, correlationId, localTemp, DURATION);
nextValue = localTemp;
}
}
}
The overall point is that changes made to nextValue immediately affect another calls of getNextIncrement.
It is really hard to debug problems in this pattern, so I may be wrong, but I post an answer anyway, since my explanation is too long for the comment.

Automic Integer Thread Clarification

I have the following code,
private final Map<String, AtomicInteger> wordCounter = new ConcurrentHashMap<>();
AtomicInteger count = wordCounter.get(word);
if (count == null) {
if ((count = wordCounter.putIfAbsent(word, new AtomicInteger(1))) == null) {
continue;
}
}
count.incrementAndGet();
I'm checking count == null in IF condition. As far as i know, operation in AutomicInteger is thread-safe. Is it necessary to lock count instance using one of the locking mechanism?
The above code works without any additional locking, but it can be simplified to the following idiomatic form
// If word doesn't exist, create a new atomic integer, otherwise return the existing
wordCounter.computeIfAbsent(word, k -> new AtomicInteger(0))
.incrementAndGet(); // increment it
Your code looks a bit like double checked locking, in that putIfAbsent() is used after the null-check to avoid overwriting a value that was possibly put there by another thread. However that path creates an extra AtomicInteger which doesn't happen with DCL. The extra object probably wouldn't matter much, but it does make the solution a little less "pure".

How to fix this race condition error? [duplicate]

This question already has an answer here:
Not thread safe methods of CuncurrentSkipListMap in Java
(1 answer)
Closed 6 years ago.
I have such simple code:
class B {
//....
}
public class A {
private ConcurrentSkipListMap<Long, B> map = new ConcurrentSkipListMap<>();
public void add(B b) {
long key = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) / 60;
//this area has bug
if (map.containsKey(key)) {
B oldB = map.get(key);
// work with oldB
} else {
map.put(key, b);
}
//end this area
}
}
So, I can get key from 2 threads. Then first thread go to else-path. Then second thread is starting. But first thread has not added value yet.
Wrap the area you have marked as "this area has a bug" in a synchronized block:
synchronized (map) {
if (map.containsKey(key)) {
B oldB = map.get(key);
// work with oldB
} else {
map.put(key, b);
}
}
This prevents two threads with the same key value from accessing the map at the same time - but only if all other accesses to the map are also synchronized with get (e.g. you don't have an unsynchronized map.get elsewhere in the class).
Note that this prevents all concurrent updates to the map, which might create an unacceptable bottleneck. Whilst you can use Long.valueOf(key) to obtain an instance on which you can synchronize, there are no guaranteed ranges of input which are guaranteed to be cached.
Instead, you could perhaps map the long into the range of values cached by Integer.valueOf (i.e. -128 to 127), which would give you a more granular lock, e.g.
// Assuming that your clock isn't stuck in the 1960s...
Integer intKey = Integer.valueOf((int)( (longKey % 255) - 128));
synchronized (intKey) {
// ...
}
(Or, of course, you could maintain your own cache of keys).

Does ConcurrentHashMap need synchronization when incrementing its values?

I know ConcurrentHashMap is thread-safe e.g.putIfAbsent,Replace etc., but I was wondering, is a block of code like the one below safe?
if (accumulator.containsKey(key)) { //accumulator is a ConcurrentHashMap
accumulator.put(key, accumulator.get(key)+1);
} else {
accumulator.put(key, 0);
}
Keep in mind that the accumulator value for a key may be asked by two different threads simultaneously, which would cause a problem in a normal HashMap. So do I need something like this?
ConcurrentHashMap<Integer,Object> locks;
...
locks.putIfAbsent(key,new Object());
synchronized(locks.get(key)) {
if (accumulator.containsKey(key)) {
accumulator.put(key, accumulator.get(key)+1);
} else {
accumulator.put(key, 0);
}
}
if (accumulator.containsKey(key)) { //accumulator is a ConcurrentHashMap
accumulator.put(key, accumulator.get(key)+1);
} else {
accumulator.put(key, 0);
}
No, this code is not thread-safe; accumulator.get(key) can be changed in between the get and the put, or the entry can be added between the containsKey and the put. If you're in Java 8, you can write accumulator.compute(key, (k, v) -> (v == null) ? 0 : v + 1), or any of the many equivalents, and it'll work. If you're not, the thing to do is write something like
while (true) {
Integer old = accumulator.get(key);
if (old == null) {
if (accumulator.putIfAbsent(key, 0) == null) {
// note: it's a little surprising that you want to put 0 in this case,
// are you sure you don't mean 1?
break;
}
} else if (accumulator.replace(key, old, old + 1)) {
break;
}
}
...which loops until it manages to make the atomic swap. This sort of loop is pretty much how you have to do it: it's how AtomicInteger works, and what you're asking for is AtomicInteger across many keys.
Alternately, you can use a library: e.g. Guava has AtomicLongMap and ConcurrentHashMultiset, which also do things like this.
I think the best solution for you would be to use an AtomicInteger. The nice feature here is that it is non-blocking, mutable and thread-safe. You can use the replace method offered by the CHM, but with that you will have to hold a lock of the segment/bucket-entry prior to the replace completing.
With the AtomicInteger you leverage quick non-blocking updates.
ConcurrentMap<Key, AtomicInteger> map;
then
map.get(key).incrementAndGet();
If you are using Java 8, LongAdder would be better.
You are correct that your first code snippet is unsafe. It's totally reasonable for the thread to get interrupted right after the check has been performed and for another thread to begin executing. Therefore in the first snippet the following could happen:
[Thread 1]: Check for key, return false
[Thread 2]: Check for key, return false
[Thread 2]: Put value 0 in for key
[Thread 1]: Put value 0 in for key
In this example, the behavior you would want would leave you in a state with the value for that key being set to 1, not 0.
Therefore locking is necessary.
Only individual actions on ConcurrentHashMap are thread-safe; taking multiple actions in sequence is not. Your first block of code is not thread-safe. It is possible, for example:
THREAD A: accumulator.containsKey(key) = false
THREAD B: accumulator.containsKey(key) = false
THREAD B: accumulator.put(key, 0)
THREAD A: accumulator.put(key, 0)
Similary, it is not thread-safe to get the accumulator value for a given key, increment it, and then put it back in the map. This is a three-step process, and it is possible for another thread to interrupt at any point.
Your second synchronized block of code is thread-safe.

Concurrent hashmap simultaneous insertion

I have read that in concurrent hashmap in Java, simultaneous insertions are possible because it is divided into segments and separate lock is taken for each segment.
But if two insertions are going to happen on same segment, then these simultaneous will not happen.
My question is what will happen in such a case? Will second insertion waits till first one gets completed or what?
In general you don't need be too concerned how ConcurrentHashMap is implemented. It simply complies to the the contract of ConcurrentMap which ensures that concurrent modifications are possible.
But to answer your question: yes, one insertion may wait for completion of the other one. Internally, it uses locks which ensure that one thread is waiting until the other one releases the lock. Class Segment used internally actually inherits from ReentrantLock. Here is a shortened version of Segmenet.put():
final V put(K key, int hash, V value, boolean onlyIfAbsent) {
HashEntry<K,V> node = tryLock() ? null : scanAndLockForPut(key, hash, value);
V oldValue;
try {
// modifications
} finally {
unlock();
}
return oldValue;
}
private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) {
// ...
int retries = -1; // negative while locating node
while (!tryLock()) {
if (retries < 0) {
// ...
}
else if (++retries > MAX_SCAN_RETRIES) {
lock();
break;
}
else if ((retries & 1) == 0 && (f = entryForHash(this, hash)) != first) {
e = first = f; // re-traverse if entry changed
retries = -1;
}
}
return node;
}
This could give you an idea.
ConcurrentHashMap does not block when performing retrieval operations, and there is no locking for the usual operations.
The heuristic with most Concurrent Data Structures is that there's a backing data structure that gets modified first, with a front-facing data structure that's visible to outside methods. Then, when the modification is complete, the backing data structure is made the public data structure and the public data structure is pushed to the back. There's way more to it than that, but that's the typical contract.
If 2 updates try to happen on the same segment they will go into contention with each other and one of them will have to wait. You can optimise this by choosing a concurrencyLevel value which takes into account the number of threads which will be concurrently updating the hashmap.
You can find all the details in the javadoc for the class
ConcurrentHashMap contains array of Segment which in turn holds array of HashEntry. Each HashEntry holds a key, a value, and a pointer to it's next adjacent entry.
But it acquires the lock in segment level. Hence you are correct. i.e second insertion waits till first one gets completed
Take a look at the javadoc for ConcurrentMap. It describes the extra methods available to deal with concurrent map mutations.

Categories