Adding to AtomicInteger within ConcurrentHashMap - java

I have the following defined
private ConcurrentMap<Integer, AtomicInteger> = new ConcurrentHashMap<Integer, AtomicInteger>();
private void add() {
staffValues.replace(100, staffValues.get(100), new AtomicInteger(staffValues.get(100).addAndGet(200)));
}
After testing, the values I am getting are not expected, and I think there is a race condition here. Does anyone know if this would be considered threadsafe by wrapping the get call in the replace function?

A good way to handle situations like this is using the computeIfAbsent method (not the compute method that #the8472 recommends)
The computeIfAbsent accepts 2 arguments, the key, and a Function<K, V> that will only be called if the existing value is missing. Since a AtomicInteger is thread safe to increment from multiple threads, you can use it easely in the following manner:
staffValues.computeIfAbsent(100, k -> new AtomicInteger(0)).addAndGet(200);

There are a few issues with your code. The biggest is that you're ignoring the return-value of ConcurrentHashMap.replace: if the replacement doesn't happen (due to another thread having made a replacement in parallel), you simply proceed as if it happened. This is the main reason you're getting wrong results.
I also think it's a design mistake to mutate an AtomicInteger and then immediately replace it with a different AtomicInteger; even if you can get this working, there's simply no reason for it.
Lastly, I don't think you should call staffValues.get(100) twice. I don't think that causes a bug in the current code — your correctness depends only on the second call returning a "newer" result than the first, which I think is actually guaranteed by ConcurrentHashMap — but it's fragile and subtle and confusing. In general, when you call ConcurrentHashMap.replace, its third argument should be something you computed using the second.
Overall, you can simplify your code either by not using AtomicInteger:
private ConcurrentMap<Integer, Integer> staffValues = new ConcurrentHashMap<>();
private void add() {
final Integer prevValue = staffValues.get(100);
staffValues.replace(100, prevValue, prevValue + 200);
}
or by not using replace (and perhaps not even ConcurrentMap, depending how else you're touching this map):
private Map<Integer, AtomicInteger> staffValues = new HashMap<>();
private void add() {
staffValues.get(100).addAndGet(200);
}

You don't need to use replace(). AtomicInteger is a mutable value that does not need to be substituted whenever you want to increment it. In fact addAndGet already increments it in place.
Instead use compute to put a default value (presumably 0) into the map when none is present and otherwise get the pre-existing value and increment that.
If, on the other hand, you want to use immutable values put Integer instances instead of AtomicInteger into the map and update them with the atomic compute/replace/merge operations.

Related

Is incrementing of current value while putting thread-safe in ConcurrentHashMap?

I wonder what happens when I modify current value while putting it into ConcurrentHashMap.
I have ConcurrentHashMap (ConncurentHashMap<String, Integer> attendance) with existing mapping of conference hall names and number of visitors to each.
Every invocation of visit(conferenceHallName) method is supposed to increment the number of visitors to the given conference hall. Every invoker is a thread.
So here is the method:
public void visit(String conferenceHallName) {
attendance.put(conferenceHallName, attendance.get(conferenceHallName) + 1);
}
put() is locking method, get() is not. But what happens first in this case:
thread will lock segment with this mapping, then calculate a new value and then put and release which is perfect for me
or
thread will get the old value, calculate a new one, then lock segment and put which means inconsistency for me and I will need to find a workaround.
And if the second scenario is what happens in reality will using AtomicInteger instead of Integer solve my issue?
The second description is closer to what actually happens: the thread will access the value in a thread-safe way, construct a new Integer with the updated count, then lock the map, and replace the object.
Using AtomicInteger instead of Integer will solve the issue:
attendance.get(conferenceHallName).getAndIncrement();
This is assuming that all conferenceHallName keys are properly set in the map.

Concurrent frequency counter - concurrency issue

I would like to create a concurrent frequency counter class in Java.
It's about that once a request is processed (by processRequest method), the code checks the request's type (an integer) and counts how many requests have been processed (grouped by the request's type) from a given time. The processRequest method will be called by multiple threads in the same time.
There are two other methods:
clearMap(): It will be called by one thread in every 3 hours and clears the whole map.
getMap(): It can be called in any time by a webservice and returns an immutable copy of the current state of the frequency map.
See below my initial plan to implement that.
public class FrequencyCounter {
private final ConcurrentHashMap<Integer,Long> frequencenyMap = new ConcurrentHashMap<>();
public void processRequest(Request request){
frequencenyMap.merge(request.type, 0L, (v, d) -> v+1);
}
public void clearMap(){
frequencenyMap.clear();
}
public Map<Integer,Long> getMap(){
return ImmutableMap.copyOf(frequencenyMap);
}
}
I checked the documentation of ConcurrentHashMap and it tells that the merge method is performed atomically.
So once the clear() method starts to clear the hash buckets of the map (locking as per hash bucket), it can't be invoked when another thread is between getting the value of the frequency map and incrementing its value in the processRequest method because the merge method is executed atomically.
Am I right?
Does my above plan seem to be fine?
Thank you for your advice.
First, replace Long with AtomicLong.
Second, use computeIfAbsent.
private final Map<Integer, AtomicLong> frequencyMap = new ConcurrentHashMap<>();
public void processRequest(Request request){
frequencyMap.computeIfAbsent(request.type, k -> new AtomicLong())
.incrementAndGet();
}
There are a few reasons why I believe this is a better solution:
The code in the question uses boxed objects, i.e. (v, d) -> v+1 is really (Long v, Long d) -> Long.valueOf(v.longValue() + 1).
That code generates extra garbage, which can be avoided by using AtomicLong.
The code here only allocates one object per key, and doesn't require any extra allocations to increment the counter, e.g. it will still only be the one object even if counter goes to the millions.
The unboxing, adding 1, boxing operation will likely take slightly longer than the tightly coded incrementAndGet() operation, increasing the likelyhood of a collision, requiring a re-try in the merge method.
Code "purity". Using a method that takes a "value", which is then entirely ignored, seems wrong to me. It is unnecessary code noise.
These are of course my opinions. You can make your own decision, but I think this code clarifies the purpose, i.e. to increment a long counter, in a fully thread-safe way.

Java CHM synchronisation

Following up on this question (Java thread safety - multiple atomic operations?), I don't want to add more questions to it, but now I have this doubt:
private final Map<String, Set<String>> data = Maps.newConcurrentMap();
... then in a method ...
if (data.containsKey("A")) {
data.get("A").add("B");
}
It should be something like this:
synchronized(data) {
if (data.containsKey("A")) {
data.get("A").add("B");
}
}
In order to be thread-safe. Is that correct?
So operations are atomic, but combining them would require synchronisation, is that right? At that point, would it make sense to just use a simple HashMap instead of a concurrent one, as we're manually handling sync?
Is there any method in CHM to make this work atomically?
In your specific case, you might want to use computeIfPresent method of ConcurrentHashMap:
data.computeIfPresent("A", (k, v) -> { v.add("B"); return v; } );
From the javadocs:
If the value for the specified key is present, attempts to compute a new mapping given the key and its current mapped value. The entire method invocation is performed atomically.
So there's no need for explicit synchronization.
synchronised(data) {
if (data.containsKey("A")) {
data.get("A").add("B");
}
}
You probably need to show more code.
Looking only at this, the only possible issue is that someone removes the Set found at "A" after your if check. If you don't ever remove map entries you need no synchronization at all.
If you do remove map entries concurrently, you could use computeIfPresent to arrive at the updated map.
You could also do
Set<String> set = data.get("A");
if (set != null) set.add("B");
Since you are not actually producting a new Set, I find this more idiomatic than computeIfPresent (which should compute a new value).
Note that you need to make all these Sets thread-safe as well.

Is the following code thread-safe? [duplicate]

This question already has answers here:
Java double checked locking
(11 answers)
Closed 7 years ago.
The following code uses a double checked pattern to initialize variables. I believe the code is thread safe, as the map wont partially assigned even if two threads are getting into getMap() method at the same time. So I don't have to make the map as volatile as well. Is the reasoning correct? NOTE: The map is immutable once it is initialized.
class A {
private Map<String, Integer> map;
private final Object lock = new Object();
public static Map<String, Integer> prepareMap() {
Map<String, Integer> map = new HashMap<>();
map.put("test", 1);
return map;
}
public Map<String, Integer> getMap() {
if (map == null) {
synchronized (lock) {
if (map == null) {
map = prepareMap();
}
}
}
return map;
}
}
According to the top names in the Java world, no it is not thread safe. You can read why here: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
You better off using ConcurrentHashmap or synchronizing your Map.
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html
Edit: If you only want to make the initialization of the map thread safe (so that two or more maps are not accidentally created) then you can do two things. 1) initialize the map when it is declared. 2) make the getMap() method synchronized.
No, your reasoning is wrong, access to the map is not thread safe, because the threads that call getMap() after the initialization may not invoke synchronized(lock) and thus are not in happens-before relation to other threads.
The map has to be volatile.
The code could be optimized by inlining to
public Map<String,Integer> getMap()
{
if(map == null)
{
synchronized(lock)
{
if(map == null)
{
map = new HashMap<>(); // partial map exposed
map.put("test", 1);
}
}
}
return map;
}
}
Having a HashMap under concurrent read and write is VERY dangerous, don't do it. Google HashMap infinite loop.
Solutions -
Expand synchronized to the entire method, so that reading map variable is also under lock. This is a little expensive.
Declare map as volatile, to prevent reordering optimization. This is simple, and pretty cheap.
Use an immutable map. The final fields will also prevent exposing partial object state. In your particular example, we can use Collections.singletonMap. But for maps with more entries, I'm not sure JDK has a public implementation.
This is just one example of how things can go wrong. To fully understand the issues, there is no substitute for reading The "Double-Checked Locking is Broken" Declaration, referenced in a prior answer.
To get anything approaching the full flavor, think about two processors, A and B, each with its own caches, and a main memory that they share.
Suppose Thread A, running on Processor A, first calls getMap. It does several assignments inside the synchronized block. Suppose the assignment to map gets written to main memory first, before Thread A reaches the end of the synchronized block.
Meanwhile, on Processor B, Thread B also calls getMap, and does not happen to have the memory location representing map in its cache. It goes out to main memory to get it, and its read happens to hit just after Thread A's assignment to map, so it sees a non-null map. Thread B does not enter the synchronized block.
At this point, Thread B can go ahead and attempt to use the HashMap, despite the fact that Thread A's work on creating it has not yet been written to main memory. Thread B may even have the memory pointed to by map in its cache because of a prior use.
If you are tempted to try to work around this, consider the following quote from the referenced article:
There are lots of reasons it doesn't work. The first couple of reasons
we'll describe are more obvious. After understanding those, you may be
tempted to try to devise a way to "fix" the double-checked locking
idiom. Your fixes will not work: there are more subtle reasons why
your fix won't work. Understand those reasons, come up with a better
fix, and it still won't work, because there are even more subtle
reasons.
This answer only contains one of the most obvious reasons.
No, it is not thread safe.
The basic reason is that you can have reordering of operations you don't even see in the Java code. Let's imagine a similar pattern with an even simpler class:
class Simple {
int value = 42;
}
In the analogous getSimple() method, you assign /* non-volatile */ simple = new Simple (). What happens here?
the JVM allocates some space for the new object
the JVM sets some bit of this space to 42 (for value)
the JVM returns the address of this space, which is then assigned to space
Without synchronization instructions to prohibit it, these instructions can be reordered. In particular, steps 2 and 3 can be ordered such that simple gets the new object's address before the constructor finishes! If another thread then reads simple.value, it'll see a value 0 (the field's default value) instead of 42. This is called seeing a partially-constructed object. Yes, that's weird; yes, I've seen things like that happen. It's a real bug.
You can imagine how if the object is a non-trivial object, like HashMap, the problem is even worse; there are a lot more operations, and so more possibilities for weird ordering.
Marking the field as volatile is a way of telling the JVM, "any thread that reads a value from this field must also read all operations that happened before that value was written." That prohibits those weird reorderings, which guarantees you'll see the fully-constructed object.
Unless you declare the lock as volatile, this code may be translated to non-thread-safe bytecode.
The compiler may optimize the expression map == null, cache the value of the expression and thus read the map property only once.
volatile Map<> map instructs the Java VM to always read the property map when it is accessed. Thsi would forbid such optimization from the complier.
Please refer to JLS Chapter 17. Threads and Locks

Constructing multiple threads to output words to a map

I have a wordCount(CharacterReader charReader) function which takes a stream of characters, converts them to words.
I also have a Collection<CharacterReader> characerReaders, containing multiple character streams. The number of readers in a collection can vary, I want to read from all streams and have a count of all words.
I'm a little confused about threads and couldn't find any examples which were similar to this.
I essentially want multiple threads outputting their words into a SortedMap so I can have a real time total word count.
How would I go about doing this?
Thanks
If you are going to have multiple threads writing to the map, you need to use a ConcurrentSkipListMap which is both a SortedMap and a ConcurrentMap.
You can create for each CharacterReader in the collection a Runnable which calls the wordCount function (which accesses the map described previously).
After creating the Runnables you can create an ExecutorService (for example using Executors.newCacheThreadPool()), pass it all the Runnables and wait for them to finish (see the example in the javadoc of class ExecutorService).
You can also create the Runnables just before sending them to the ExecutorService.
Create a WordMap class which encapsulates your sorted map, and makes sure all the accesses to the map are properly synchronized. Or use a concurrent map that is already thread safe.
Create an instance of this class. Use the Executors class to create an ExecutorService with the characteristics that you desire.
Then iterate through the collection, and for each reader, create a Callable or a Runnable filling the WordMap instance with the words found in this reader, and submit this Callable or Runnable to the ExecutorService.
vainolo and JB's answers are both good.
I will add one thing, which is a description of how to make a highly concurrent data structure to store your word counts.
As vainolo said, a ConcurrentSkipListMap is the basic data structure you want, because it is both sorted and concurrent. To make good use if it, you want to avoid doing any locking. That means you must avoid patterns which involve a lock-read-write-unlock cycle. That has two consequences: firstly, putting a new word in the map should not involve a lock, and incrementing the count of an existing word should not involve a lock.
You can safely add new things to the map using ConcurrentMap's putIfAbsent method. However, that alone is not quite enough, because you have to supply a potential value every time you use it, which is potentially expensive. The easiest thing to do is to use a sort of double-checked locking pattern, where you first simply try to get an existing value, then if you find there isn't one, add a new one with putIfAbsent (you can't simply call put, because there could be a race between two threads putting at the same time).
Incrementing without locking can easily be done by not storing integers in the map, but rather objects which themselves contain integers. That way, you never have to put an incremented value in the map, you just increment the object already there. AtomicInteger seems like a good candidate for this.
Putting that together, you get:
public class WordCounts {
private final ConcurrentMap<String, AtomicInteger> counts
= new ConcurrentSkipListMap<String, AtomicInteger>();
public void count(String word) {
AtomicInteger count = getCount(word);
count.incrementAndGet();
}
private AtomicInteger getCount(String word) {
AtomicInteger count = counts.get(word);
if (count == null) {
AtomicInteger newCount = new AtomicInteger();
count = counts.putIfAbsent(word, newCount);
if (count == null) count = newCount;
}
return count;
}
}

Categories