I have a stateful bean in an multi-threaded enviroment, which keeps its state in a map. Now I need a way to replace all values of that map in one atomic action.
public final class StatefulBean {
private final Map<String, String> state = new ConcurrentSkipListMap<>();
public StatefulBean() {
//Initial state
this.state.put("a", "a1");
this.state.put("b", "b1");
this.state.put("c", "c1");
}
public void updateState() {
//Fake computation of new state
final Map<String, String> newState = new HashMap<>();
newState.put("b", "b1");
newState.put("c", "c2");
newState.put("d", "d1");
atomicallyUpdateState(newState);
/*Expected result
* a: removed
* b: unchanged
* C: replaced
* d: added*/
}
private void atomicallyUpdateState(final Map<String, String> newState) {
//???
}
}
At the moment I use ConcurrentSkipListMap as implementation of a ConcurrentMap, but that isn't a requirement.
The only way I see to solve this problem is to make the global state volatile and completely replace the map or use a AtomicReferenceFieldUpdater.
Is there a better way?
My updates are quite frequent, once or twice a second, but chance only very few values. Also the whole map will only ever contain fewer than 20 values.
Approach with CAS and AtomicReference would be to copy map content on each bulk update.
AtomicReference<Map<String, String>> workingMapRef = new AtomicReference<>(new HashMap<>());
This map can be concurrent, but for "bulk updates" it is read-only. Then in updateState looping doUpdateState() until you get true and that means that your values has been updated.
void updateState() {
while (!doUpdateState());
}
boolean doUpdateState() {
Map<String, String> workingMap = workingMapRef.get();
//copy map content
Map<String, String> newState = new HashMap<>(workingMap); //you can make it concurrent
newState.put("b", "b1");
newState.put("c", "c2");
newState.put("d", "d1");
return workingMapRef.compareAndSet(workingMap, newState);
}
The simplest, least fuss method is to switch the map instead of replacing map contents. Whether using volatile or AtomicReference (I don't see why you'd need AtomicReferenceFieldUpdater particularly), shouldn't make too much of a difference.
This makes sure that your map is always in proper state, and allows you to provide snapshots too. It doesn't protect you from other concurrency issues though, so if something like lost updates are a problem you'll need further code (although AtomicReference would give you CAS methods for handling those).
The question is actually rather simple if you only consider the complete atomic replacement of the map. It would be informative to know what other operations affect the map and how. I'd also like to hear why ConcurrentSkipListMap was chosen over ConcurrentHashMap.
Since the map is quite small, it's probably enough to just use synchronized in all places you access it.
private void atomicallyUpdateState(final Map<String, String> newState) {
synchronized(state) {
state.clear();
state.putAll(newState);
}
}
but don't forget any, like all occurances of things like
String myStatevalue = state.get("myValue");
need to become
String myStatevalue;
synchronized (state) {
myStatevalue = state.get("myValue");
}
otherwise the read and update are not synchronized and cause a race condition.
Extend a map implementation of your choice and add a synchronized method:
class MyReplaceMap<K, V> extends HashMap<K, V> //or whatever
{
public synchronized void replaceKeys(final Map<K, V> newMap)
{
//.. do some stuff
}
}
Of course, you could always make state non-final volatile and re-assign it (assignment is atomic)
private volatile Map<String, String> state = new HashMap<>();
//...
final Map<String, String> newState = new HashMap<>();
newState.put("b", "b1");
newState.put("c", "c2");
newState.put("d", "d1");
state = newState;
As client code maintains a reference to the bean not the map, replacing the value (i.e. the whole map) would seem to be the simplest solution.
Unless there's any significant performance concerns (although using locking is likely to perform worse and less predictably unless the map is huge) I'd try that before anything requiring more advanced knowledge.
It's how a functional programmer would do it.
Use ReadWriteLock can help to automically replace all values in a Map.
private static final ReadWriteLock LOCK = new ReentrantReadWriteLock();
private void atomicallyUpdateState(final Map<String, String> newState) {
LOCK.writeLock().lock();
try {
state.clear();
state.putAll(newState);
} finally {
LOCK.writeLock().unlock();
}
}
Related
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.
I'm maintaining multi-threaded legacy code that uses ConcurrentHashMap.
There are operations of add and remove in other methods.
In the following code, at some point after collecting few values from the map, it throws NullPointerException when executing synchronize(value).
public class MyClass{
private final Map<MyObj, Map<String, List<String>>> conMap = new ConcurrentHashMap<>();
//...
public void doSomthing((MyObj id){
List<Map<String, List<String>>> mapsList = new LinkedList<>();
for(MyObj objId: conMap.keySet()){
if(objId.key1.equals(id.key1)){
mapsList.add(conMap.get(objId));
}
}
for(Map<String, List<String>> map: mapsList){
synchronized(map){ // <-- NullPointerException here
//...
}
}
//...
}
I have a feeling that maybe during the iteration in the first loop, records are being remove. And when the line:
mapsList.add(conMap.get(objId));
being executed, objId no longer exist and mapsList adding null and as a result, during the second loop NullPoinerException is thrown.
Is there any other reason to get this exception?
You have fallen for the Check-Then-Act anti-pattern. It implies checking a condition (like the presence of a key), followed by acting upon it (like calling get), ignoring the possibility that the condition may have changed in-between.
So you encounter a particular key when iterating over conMap.keySet(), but by the time you’re invoking conMap.get(objId), the key might not be in the map anymore, which is reported by returning null.
It’s strongly recommended to use a key type having a suitable hashCode/equals implementation, so you don’t need to iterate over the entire map to find matches but can use a single get(id).
However, when you have to iterate over the map and need the values, iterate over the entry set instead of the key set.
public void doSomething(MyObj id){
// see https://stackoverflow.com/q/322715/2711488
List<Map<String, List<String>>> mapsList = new ArrayList<>();
for(Map.Entry<MyObj, Map<String, List<String>>> e: conMap.entrySet()){
if(e.getKey().key1.equals(id.key1)){
mapsList.add(e.getValue());
}
}
for(Map<String, List<String>> map: mapsList){
synchronized(map) {
//...
}
}
}
I have a stateful bean in an multi-threaded enviroment, which keeps its state in a map. Now I need a way to replace all values of that map in one atomic action.
public final class StatefulBean {
private final Map<String, String> state = new ConcurrentSkipListMap<>();
public StatefulBean() {
//Initial state
this.state.put("a", "a1");
this.state.put("b", "b1");
this.state.put("c", "c1");
}
public void updateState() {
//Fake computation of new state
final Map<String, String> newState = new HashMap<>();
newState.put("b", "b1");
newState.put("c", "c2");
newState.put("d", "d1");
atomicallyUpdateState(newState);
/*Expected result
* a: removed
* b: unchanged
* C: replaced
* d: added*/
}
private void atomicallyUpdateState(final Map<String, String> newState) {
//???
}
}
At the moment I use ConcurrentSkipListMap as implementation of a ConcurrentMap, but that isn't a requirement.
The only way I see to solve this problem is to make the global state volatile and completely replace the map or use a AtomicReferenceFieldUpdater.
Is there a better way?
My updates are quite frequent, once or twice a second, but chance only very few values. Also the whole map will only ever contain fewer than 20 values.
Approach with CAS and AtomicReference would be to copy map content on each bulk update.
AtomicReference<Map<String, String>> workingMapRef = new AtomicReference<>(new HashMap<>());
This map can be concurrent, but for "bulk updates" it is read-only. Then in updateState looping doUpdateState() until you get true and that means that your values has been updated.
void updateState() {
while (!doUpdateState());
}
boolean doUpdateState() {
Map<String, String> workingMap = workingMapRef.get();
//copy map content
Map<String, String> newState = new HashMap<>(workingMap); //you can make it concurrent
newState.put("b", "b1");
newState.put("c", "c2");
newState.put("d", "d1");
return workingMapRef.compareAndSet(workingMap, newState);
}
The simplest, least fuss method is to switch the map instead of replacing map contents. Whether using volatile or AtomicReference (I don't see why you'd need AtomicReferenceFieldUpdater particularly), shouldn't make too much of a difference.
This makes sure that your map is always in proper state, and allows you to provide snapshots too. It doesn't protect you from other concurrency issues though, so if something like lost updates are a problem you'll need further code (although AtomicReference would give you CAS methods for handling those).
The question is actually rather simple if you only consider the complete atomic replacement of the map. It would be informative to know what other operations affect the map and how. I'd also like to hear why ConcurrentSkipListMap was chosen over ConcurrentHashMap.
Since the map is quite small, it's probably enough to just use synchronized in all places you access it.
private void atomicallyUpdateState(final Map<String, String> newState) {
synchronized(state) {
state.clear();
state.putAll(newState);
}
}
but don't forget any, like all occurances of things like
String myStatevalue = state.get("myValue");
need to become
String myStatevalue;
synchronized (state) {
myStatevalue = state.get("myValue");
}
otherwise the read and update are not synchronized and cause a race condition.
Extend a map implementation of your choice and add a synchronized method:
class MyReplaceMap<K, V> extends HashMap<K, V> //or whatever
{
public synchronized void replaceKeys(final Map<K, V> newMap)
{
//.. do some stuff
}
}
Of course, you could always make state non-final volatile and re-assign it (assignment is atomic)
private volatile Map<String, String> state = new HashMap<>();
//...
final Map<String, String> newState = new HashMap<>();
newState.put("b", "b1");
newState.put("c", "c2");
newState.put("d", "d1");
state = newState;
As client code maintains a reference to the bean not the map, replacing the value (i.e. the whole map) would seem to be the simplest solution.
Unless there's any significant performance concerns (although using locking is likely to perform worse and less predictably unless the map is huge) I'd try that before anything requiring more advanced knowledge.
It's how a functional programmer would do it.
Use ReadWriteLock can help to automically replace all values in a Map.
private static final ReadWriteLock LOCK = new ReentrantReadWriteLock();
private void atomicallyUpdateState(final Map<String, String> newState) {
LOCK.writeLock().lock();
try {
state.clear();
state.putAll(newState);
} finally {
LOCK.writeLock().unlock();
}
}
Is it a bad practice to instantiate private variable in a class to avoid the NullPointerException when using the variable, for example:
public Class MyClass{
private HashMap<String, String> myHashMap = new HashMap<String, String>();
public HashMap<String, String> getMyHashMap (){return myHashMap; }
public HashMap<String, String> setMyHashMap (String myString){ /* treatment to set the myHashMap under some conditions */}
}
If we don't instantiate the myHashMap, the method getMyHashMap() may return null
Is it a better practice that the caller of getMyHashMap() checks for the null value?
Is there a coding good practice to be applied in those circumstances?
I use two solutions for this kind of situations. If myHashMap has to be always available, set it as final and initialize with value:
private final HashMap<String, String> myHashMap = new HashMap<String, String>();
public HashMap<String, String> getMyHashMap () {
return myHashMap;
}
If myHashMap is something bigger than empty object I rather preffer to do something like a lazy loading:
private HashMap<String, String> myHashMap;
public HashMap<String, String> getMyHashMap () {
if (myHashMap == null) {
myHashMap = new HashMap<String, String>();
}
return myHashMap;
}
It is perfectly ok.
A common pattern in programming is iterating over a collection or map that is returned from a method. If the method possibly returns null, the onus is on the caller to perform a null check before attempting to iterate over the collection or map. Thus, it is normally considered bad practice to return null from public methods that return a collection or map. A better alternative would be to return an empty collection or map, as these allow safe iteration without a NullPointerException.
While not recommended, it is ok to return null from a private method that returns a collection or map, since their scope is limited to the enclosing class.
No. It is not a bad practice.
By doing this, you are establishing up an invariant for the class: myHashMap is not null. This invariant makes reasoning about the correct functioning of the rest of the class easier: you know that myHashMap is not null, so you won't get NullPointerException when you try to access it.
You can implement this by assigning the value in an initializer block (which your code is equivalent to) or assigning the value in the constructor. The two are roughly equivalent; actually, assigning the field when you declare it becomes an initializer block, which is executed before the constructor.
You can strengthen your reliance upon this invariant by making myHashMap final: this means that you can't accidentally set it to null later.
You don't strictly need this invariant: as #hsz suggests, you can make the value non-null lazily. However, I would argue that this clutters the class, and makes it harder to reason about. It's also harder to change the class to add new functionality: you have to remember to add that lazy instantiation to new code if it requires access to myHashMap.
I would say it is much better to create it once-and-for-all when you create the instance, and get on with the rest of your code - especially for something as trivial as an empty HashMap.
You could instantiate your map in the constructor.
public Class MyClass{
private HashMap<String, String> myHashMap;
public MyClass(){
myHashMap = new HashMap<String, String>();
}
public HashMap<String, String> getMyHashMap (){ return myHashMap; }
public HashMap<String, String> setMyHashMap (String myString){ /* treatment to set the myHashMap under some conditions */}
}
This way, getMyHashMap will never return null
Below is a class that holds a map of misspelled to correctly spelled terms. The map is updated periodically by a quartz job by calling updateCache(). Method updatecache processes key and values in the input map and stores them in a temporary map object. After the processing is complete (after for loop), it assigns the temporary map to local class variable misspelledToCorrectlySpelled.
package com.test;
import java.util.HashMap;
import java.util.Map;
import org.checkthread.annotations.ThreadSafe;
#ThreadSafe
public class SpellCorrectListCacheManager {
private Map<String, String> misspelledToCorrectlySpelled =
new HashMap<String, String>(0);
/*
* invoked by a quartz job thread
*/
public void updateCache(Map<String, String> map) {
Map<String, String> tempMap = new HashMap<String, String>(map.size());
for (Map.Entry<String, String> entry : map.entrySet()) {
//process key and values
String key = entry.getKey().toLowerCase();
String value = entry.getValue().toLowerCase();
tempMap.put(key, value);
}
// update local variable
this.misspelledToCorrectlySpelled = tempMap;
}
/*
* Could be invoked by *multiple* threads
*/
public Map<String, String> getMisspelledToCorrectlySpelled() {
return misspelledToCorrectlySpelled;
}
}
Question 1: Will JIT optimize optimize this code?
Actual Code
/*
* since tempMap is assigned to misspelledToCorrectlySpelled and not
* used anywhere else, will JIT remove tempMap as shown in the optimized
* version below?
*/
Map<String, String> tempMap = new HashMap<String, String>(map.size());
for (Map.Entry<String, String> entry : map.entrySet()) {
// process key and values
String key = entry.getKey().toLowerCase();
String value = entry.getValue().toLowerCase();
tempMap.put(key, value);
}
this.misspelledToCorrectlySpelled = tempMap;
Optmized Code
this.misspelledToCorrectlySpelled = new HashMap<String, String>(map.size());
for (Map.Entry<String, String> entry : map.entrySet()) {
//process key and values
String key = entry.getKey().toLowerCase();
String value = entry.getValue().toLowerCase();
this.misspelledToCorrectlySpelled.put(key, value);
}
Question 2: Assuming JIT won't optimize the code, should method getMisspelledToCorrectlySpelled be synchronized?
/*
* is this assignment atomic operation?
*
* Does this needs to be synchronized?
*
* By not synchronizing, the new map may not
* be visible to other threads *immediately* -- this is
* ok since the new map will be visible after a bit of time
*
*/
this.misspelledToCorrectlySpelled = tempMap;
}
You should use an AtomicReference to store the new map, in order to avoid synchronization and visibility problems. But the biggest problem in your code is that you give access to a non-thread-safe mutable map to several threads. You should wrap your map into an unmodifiable map :
private AtomicReference<Map<String, String>> misspelledToCorrectlySpelled =
new AtomicReference<Map<String, String>>(Collections.unmodifiableMap(new HashMap<String, String>(0)));
/*
* invoked by a quartz job thread
*/
public void updateCache(Map<String, String> map) {
Map<String, String> tempMap = new HashMap<String, String>(map.size());
for (Map.Entry<String, String> entry : map.entrySet()) {
//process key and values
String key = entry.getKey().toLowerCase();
String value = entry.getValue().toLowerCase();
tempMap.put(key, value);
}
// update local variable
this.misspelledToCorrectlySpelled.set(Collections.unmodifiableMap(tempMap));
}
/*
* Could be invoked by *multiple* threads
*/
public Map<String, String> getMisspelledToCorrectlySpelled() {
return misspelledToCorrectlySpelled.get();
}
To answer your question about JIT optimization : no, the JIT won't remove the temp map usage.
Synchronization is required where synchronization is required. Nothing about the JIT can be assumed except that a compliant implementation will be compliant with the JLS and Java Memory Model and will honor the code designed with these rules. (There are multiple methods of synchronization, not all utilize the synchronized keyword.)
Synchronization is required here unless it's "okay" that a stale version is seen. (That is likely not the case here, and it could be a very stale version with caches and all -- so not something to bet on!). The "assignment of the reference" itself is atomic insofar as no "partial write" can occur, but it is not guaranteed to be [immediately] propagated ("visible") across all threads.
Happy coding.