Is changing the value of a Map an atomic operation? - java

I was wondering if synchronization or using a concurrent class is necessary, or conversely is it thread safe to use a non concurrent class and do no synchronization on a map in a multi threaded environment, if the only modification to the map is changing the values of the map.
The reason I ask this is the HashMap ( and other non concurrent maps documentation ) have this comment:
Note that this implementation is not synchronized.
If multiple threads access a hash map concurrently, and at least one of
the threads modifies the map structurally, it must be
synchronized externally. (A structural modification is any operation
that adds or deletes one or more mappings; merely changing the value
associated with a key that an instance already contains is not a
structural modification.) This is typically accomplished by
synchronizing on some object that naturally encapsulates the map.
Which makes me believe if the modification is not structural (i.e. There is no added or deleted) I should be able to update the (non concurrent) map sans synchronization.
Am I reading this correct? i.e. Is Updating of a value in a map an atomic process?

Updating a map value is not an atomic process. However, having multiple different threads each try to modify map values concurrently will not result in very strange exceptions or errors due to concurrency errors. For example, you won't cause one of the key/value pairs to disappear, or delete random elements out of the map.
However, the updates made by one thread when updating a key/value pair will not necessarily be visible to other threads unless there is some other synchronization going on (for example, if the values are things like AtomicIntegers). On top of this, there's no guarantee that the thread will even see its own updates, since they might get clobbered by some other thread.
Hope this helps!

Putting something in a HashMap is not an atomic operation:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
It may be worthwhile to wrap your HashMap with Collections#synchronizedMap.

Implementations of map such as HashMap, TreeMap, etc are not atomic nor thread safe when it comes to updates, but you may achieve atomic update operations when you're using a ConcurrentHashMap since Java 1.8.
The following method for example will add the value for a specific key or set the value if no previous value was available for the key.
ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
int addValue(String key, int value) {
return map.compute(key, (k, v) -> v == null ? value : v + value);
}

Related

ConcurrentHashMap, find by value, compare fields and put

How can I check if there is a value using the fields of a given value? And put new one?
In ConcurrentHashMap, cause I have N threads.
Here is an example of what I want. However, it is not thread-safe.
Map<Integer, Record> map = new ConcurrentHashMap<>();
// it works, but I think it's unsafe
int get(Object key) {
for (Map.Entry<Integer, Record> next : map.entrySet()) {
if (next.getValue().a == key) {
return next.getValue().b;
}
}
int code = ...newCode();
map.put(code, new Record(...))
return code;
}
record Record(Object a, int b) {
}
What you're suggesting would defeat the purpose of using a HashMap since you're iterating through the Map instead of retrieving from the Map.
What you should really do is create a new Map where the field in Record.a is the Key and the field in Record.B is the value (or just the whole Record). Then just update your logic to insert into both Maps appropriately.

Should we always use a ConcurrentHashMap when using multiple threads?

If I have a hash map and this method:
private Map<String, String> m = new HashMap<>();
private void add(String key, String value) {
String val = m.get(key);
if (val == null) {
m.put(key, value);
}
}
If I have two threads A and B calling the method with the same key and value, A and B may both see that the key is not in the map, and so may both write to the map simultaneously. However, the write order (A before B or B before A) should not affect the result because they both write the same value. But I am just wondering whether concurrent writes would be dangerous and could lead to unexpected results. In that case I should maybe use a ConcurrentHashMap.
Yes, you should use a ConcurrentHashMap (which is internally thread-safe), and use the m.putIfAbsent(key, value) of it.
m should also be final, to avoid that it is being reassigned.

HashMap implementation with the soft referenced values

I wanted to have a Map with key mapping to quite a big object.
Since the map is going to be used as a cache, I wanted to make the values/entries referenced via soft links (java.lang.ref.SoftReference) to clear it on pure memory. But in this case, I need to have my own implementation of computeIfAbsent() method.
I could implement it in the following way:
Map<Integer, SoftReference<T>> myMap = new HashMap<>();
public T get(Integer key) {
SoftReference<T> value = myMap.get(key);
if (value == null || value.get() == null) {
value = new SoftReference(retrieveValue());
myMap.put(key, value);
}
return value.get();
}
Just wanted to know, is there an out of the box solution for such a Map, like java.util.WeakHashMap?
Thanks!
Yes, Guava's CacheBuilder supports both SoftReference and WeakReference values, as well as other eviction policies based on size and time. You can use the Cache directly, or view it as a Map:
ConcurrentMap<Integer, V> map = CacheBuilder.newBuilder()
.softValues()
.build()
.asMap()

How to find a key that corresponds to a value in a hashmap without iterating the table (Java)

I'm trying to create an extension to HashMap that modifies the put function. The goal is to have the put function not allow duplicate values. So, if someone tried to insert the key/value (B,a) when the key/value (A,a) was already in the map, then it would replace the key /value (A,a) with (B,a) rather than create a new key/value pair.
The problem I'm having is that I don't know how to find a key that corresponds with a value without iterating the table (which I can't do since table is a private variable and I can't access it from within my function extension).
I have also tried to retrieve the set of keys with keySet() with the intention of running get() operations on every single key, but the set variable is really confusing me and I'm not sure how to correctly iterate and run get() operations on the individual elements.
EDIT:
Here is what I have so far:
import java.util.*;
public class UniqueHashMap extends HashMap {
public V put(K key, V value) {
boolean contains = containsValue(value);
if (contains == true)
// INSERT CODE HERE
else
return super.put(key, value);
}
}
In the if statement, what I want to do is be able to:
1) Locate the key corresponding to "value".
2) Delete the key.
3) Insert the new key/value pair.
The part I'm having trouble with is 1) because most of the solutions I've seen need access to the map (which I don't know how to get since it's a private variable in HashMap). I can get access to the keys using keySet(), but I don't know how to iteratively run get() operations on the individual keys because I am confused about the set variable.
One way to do it:
import java.util.HashMap;
import java.util.Map.Entry;
public class MyHashMap<K, V> {
private HashMap<K, V> map;
void put(K key, V value) {
if (map.containsValue(value)) {
K keyToRemove = findKeyByValue(value);
map.remove(keyToRemove);
map.put(key, value);
} else {
map.put(key, value);
}
}
private K findKeyByValue(V val) {
for (Entry<K, V> e : map.entrySet()) {
if (val == e.getValue())
return e.getKey();
}
return null;
}
}
But note that this way you will lose HashMap's constant complexity of put method.
EDIT: Included a compilable class
I think what you are looking for is the entrySet() method of Map. This allows you to iterate over the pairs of key and corresponding value together:
public void put(K key, V val) {
Iterator<Map.Entry<K, V>> entries = entrySet().iterator();
while (entries.hasNext()) {
if (entries.next().getValue().equals(val)) {
entries.remove();
break;
}
}
super.put(key, val);
}
You could extend HashMap (might be better to implement the Map yourself and use a HashMap to implement it incase they add more add methods), and override the add methods and on each one:
If HashMap contains the key already
grab the instance that the key refers to and remove it from the map, and re-put that instance into the map with the supplied key instead of the old key
Else
Just put with the supplied key and value.
Keep track of what value was last associated with the key with an internal HashSet, this is so you can do an O(1) remove for that If portion above.
Use Google Guava's BiMap. It was designed to handle this case as it is a bi-directional map.
You would call Object key = myBiMap.inverse().get( myValue );.

Is a Guava Table thread safe when its backing maps are thread safe?

Will Guava's Tables.newCustomTable(Map, Supplier) method return thread safe tables when supplied with thread safe maps? For example:
public static <R, C, V> Table<R, C, V> newConcurrentTable() {
return Tables.newCustomTable(
new ConcurrentHashMap<R, Map<C, V>>(),
new Supplier<Map<C, V>>() {
public Map<C, V> get() {
return new ConcurrentHashMap<C, V>();
}
});
}
Does that code actually return concurrent tables?
From the doc: "If multiple threads access this table concurrently and one of the threads modifies the table, it must be synchronized externally."
Concurrent backing collections aren't enough.
Kevin Bourrillion is right. The technical reason for the map you've constructed not to be thread safe is that even if the maps you are using are thread safe, the table operations may not be. Let me give an example of put, as implemented in the StandardTable, which is used by Tables.newCustomTable:
public V put(R rowKey, C columnKey, V value) {
Map<C, V> map = backingMap.get(rowKey);
if (map == null) {
map = factory.get();
backingMap.put(rowKey, map);
}
return map.put(columnKey, value);
}
Thread safety is compromised in the handling of the map == null case. Namely, two or more threads could enter that block and create a new entry for the columnKey and the last one to perform a backingMap.put(rowKey, map) would ultimately override the entry for the columnKey in the backingMap, which would lead to the loss of put operations performed by other threads. In particular the result of this operation in a multithreaded environment is non-deterministic, which is equivalent to saying that this operation is not thread safe.
The correct implementation of this method would be:
public V put(R rowKey, C columnKey, V value) {
ConcurrentMap<C, V> map = table.get(rowKey);
if (map == null) {
backingMap.putIfAbsent(rowKey, factory.get());
}
map = backingMap.get(rowKey);
return map.put(columnKey, value);
}
I'm currently investigating if it is possible to use the ForwardingTable implementation together with what you've wanted to do, to get a properly thread safe ConcurrentTable.
But to be honest, I think the reason there is no thread-safe implementation of the Table is that the interface itself doesn't provide any concurrency constructs, such as putIfAbsent or replace.

Categories