I'm trying to find an answer to these, but not able to understand(or confirm) it on Google or in Java docs.
My implementation looks like as this:
Map<String, POJO> map = new ConcurrentHashMap<String, POJO>();
if I do
value1 = map.get(key1);
value1.setProp(prop);
Any other thread may override.
Now, I'm thinking if I do like follows: would it be an atomic operation/in another words, it will block the key1 segment?
map.compute(key1, (key1, value1) -> { value1.setProp(prop) });
Javadoc for compute
function
Attempts to compute a mapping for the specified key and its current mapped value (or null if there is no current mapping). The entire method invocation is performed atomically. Some attempted update operations on this map by other threads may be blocked while computation is in progress, so the computation should be short and simple, and must not attempt to update any other mappings of this Map.
References:
1.https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html 2.https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html#compute-K-java.util.function.BiFunction-
for my final implementation, I did something like this as all threads share NewMap and at the end, I new list of POJOs
Abstract Data Type
public class NewMap {
private Map<String, POJO> map;
private boolean isUpdatable;
void NewMap(){
isUpdatable = true;
map = new ConcurrentHashMap();
}
void putPOJOProp1(String key, Type value) throws ReadOnlyException{
map.compute(key, (k,v) -> {
if(!isUpdatable) throw new ReadOnlyException();
if(k == null){
POJO p = new POJO();
p.setProp1(value);
v = p;
} else {
v = v.setProp1(v)
}
});
}
void putPOJOProp2....
void putPOJOProp3....
List<POJO> getAll() {
isUpdatable = false;
List<POJO> pojos;
for(key: map.getKeys()) {
Pojo p = map.get(key);
p.setKey(key);
pojos.add(p);
}
return pojos;
}
}
ConcurrentHashMap class is thread-safe i.e. multiple threads can operate on a single object without any complications. At a time any number of threads are applicable for a read operation without locking the ConcurrentHashMap object which is not there in HashMap.
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!
The compute(Key, BiFunction) method of ConcurrentHashMap class is used to compute a mapping for the specified key and its current mapped value (or null if there is no current mapping is found). This method is used to atomically update a value for given key in ConcurrentHashMap.
The main reason that nulls aren't allowed in ConcurrentMaps (ConcurrentHashMaps, ConcurrentSkipListMaps) because there will be ambiguities that may be just barely tolerable in non-concurrent maps can't be accommodated. The main one is that if map.
Your confusing two different terms.ConcurrentHashMap
prevents corruption of internal structure, but doesn't prevent race conditions. If all you want to avoid is data corruption, then use of ConcurrentHashMap
is valid.
But from your question it seems that you're trying to avoid race conditions. ConcurrentHashMap
doesn't protect you from them in any way.
To understand it better, let's see the following example:
Map<String, POJO> map = new ConcurrentHashMap<>();
ExecutorService pool = Executors.newWorkStealingPool(10);
for (int t = 0; t < 10; t++) {
pool.execute(() -> {
for (int i = 0; i < 100_000; i++) {
map.compute("A", (k, v) -> {
if (v == null) {
return new POJO();
}
v.a = ++v.a;
v.b = ++v.b;
return v;
});
}
});
}
pool.awaitTermination(5, TimeUnit.SECONDS);
System.out.println(map);
// With POJO like
class POJO {
// toString() here
Integer a = 1;
Integer b = 1;
}
Here we get {A=POJO{a=1000000, b=1000000}}
, so our operation was thread safe. If that's all you want you get, you're fine.
The Javadoc for ConcurrentHashMap.compute states
The entire method invocation is performed atomically.
Note, by comparison the ConcurrentSkipListMap.compute
is NOT atomic.
A more compact form of Alexey Soshin's answer is
Map<String, long[]> map = new ConcurrentSkipListMap<>();
map.put("A", new long[2]);
IntStream.range(0, 1_000_000)
.parallel()
.forEach(i -> map.compute("A", (k, v) -> {
v[0]++;
v[1]++;
return v;
}));
System.out.println(Arrays.toString(map.get("A")));
prints something like
[643553, 597254]
c.f. HashMap produces something like
[244786, 245993]
However, use a ConcurrentHashMap
and you get the expected
[1000000, 1000000]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With