Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sharing one instance of a hashmap between all instances of spring service class

I intend to create a realtime counter. So one user can incremenet the counter value for specific key. Whilst another gets the updated count value via an ajax request (either in a loop, or using some long polling method). I will use a spring controller, which will inject the service class can I do something like below, or is there a better way :

@Service
public MyService{

//instance variable in spring injected service class, not sure if this correct
static final Map<String, Integer> myMap;


public void add(String key){
  Integer count = myMap.get(key);
  count++;
  myMap.put(key, count);
}

//accessed via ajax loop (and controller), if value changes update display
public Integer getCount(String key){
  return myMap.get(key)
}

@PostConstruct
public load(){
  myMap = new HashMap<String, Integer>(10){{//initialize}};
}

Edit there are a few answers but it is not clear which is the best : Synchronize the add method ? Create map in another class(annotated repository) and inject that ? Something else ?

like image 584
NimChimpsky Avatar asked Dec 28 '25 21:12

NimChimpsky


2 Answers

You can, but need to be aware of those problems:

  • the map is empty initially, but you never check for null counters;
  • the add() method doesn't modify the counter in the map. You need to put the counter back in the map after incrementing it, since Integer is immutable. Or you need to store mutable counters inside the map
  • several threads are accessing the map without any kind of synchronization, which will lead to bugs, erratic behavior, or exceptions
  • this strategy will obviously fail in case your app is clustered among several servers
like image 192
JB Nizet Avatar answered Dec 30 '25 13:12

JB Nizet


The Integer class is immutable. This means that you can't make modifications to it. So, in order to increment the count, you have to put it back into the map after you've incremented it:

public void add(String key){
  Integer count = myMap.get(key);
  count++;
  myMap.put(key, count);
}

A problem that this introduces is thread safety. If this service class is going to be accessed by multiple threads at the same time, then you have to make sure its data is accessed in a safe way. Since the myMap is being modified, and because the HashMap class is not thread-safe, you have to make it thread-safe. One way you can do this is to use the Collections.synchronizedMap() method. This will automatically make a Map instance thread-safe.

@PostConstruct
public load(){
  myMap = new HashMap<String, Integer>(10){{//initialize}};
  myMap = Collections.synchronizedMap(myMap);
}
like image 21
Michael Avatar answered Dec 30 '25 14:12

Michael



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!