Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ConcurrentHashMap update exists value thread safe

I want to use the concurrent hash map holding some results,

ConcurrentHashMap<Long,AtomicInteger>

add a new entry if key not exists,or get value by key and increment,like this:

if(map.contains(key))
  map.get(key).addAndGet(1);
else
  map.put(key,new AtomicInteger(1));    

the put operation is not thead safe,how to solve this problem? Is put operation should within synchronized block?

like image 355
raymond Avatar asked Mar 29 '16 07:03

raymond


People also ask

Is ConcurrentHashMap values thread-safe?

The ConcurrentHashMap operations are thread-safe. ConcurrentHashMap doesn't allow null for keys and values.

How does ConcurrentHashMap ensure thread-safety?

Concurrent. ConcurrentHashMap class achieves thread-safety by dividing the map into segments, the lock is required not for the entire object but for one segment, i.e one thread requires a lock of one segment. In ConcurrentHashap the read operation doesn't require any lock.

Can two threads update the ConcurrentHashMap simultaneously?

Having two threads that change the map at the very same point time is not possible. Because the code within that ConcurrentHashMap will not allow two threads to manipulate things in parallel!

How many threads can be executed ConcurrentHashMap?

The ConcurrentHashMap class allows multiple threads to access its entries concurrently. By default, the concurrent hashmap is divided into 16 segments. This is the reason why 16 threads are allowed to concurrently modify the map at the same time.


2 Answers

The put() operation itself is implemented in a threadsafe way, i.e. if you put the same key it will be synchronized internally.

The call, however, isn't, i.e. two threads could add a new key simultaneously. You could try putIfAbsent() and if you get a return value (i.e. not null) you could call the get method. Thus you could change your code like this:

//this only adds a new key-value pair if there's not already one for the key
if( map.putIfAbsent(key,new AtomicInteger(1)) != null ) {    
  map.get(key).addAndGet(1);
}

Alternatively if you're using Java 8 you could use the compute() method which according to the JavaDoc is performed atomically. The function you pass would then check whether the value already exists or not. Since the whole call is synchronized you probably wouldn't even need to use a AtomicInteger (depends on what else you are doing with the value).

like image 116
Thomas Avatar answered Oct 06 '22 00:10

Thomas


In Java 8 you could use ConcurrentHashMap's computeIfAbsent to provide initial value:

map.computeIfAbsent(key, new AtomicInteger(0)).addAndGet(1)
like image 30
cakraww Avatar answered Oct 06 '22 01:10

cakraww