Digital Alchemist
Digital Alchemist

Reputation: 2332

Volatile to be used with Concurrent Collection?

I am developing a metrics store (Map) , which basically collects metrics about some operations such as

Here Key is the name of the method and value are metrics about it.

Spring can help me create singleton object of MetricStore, i am using ConcurrentHashMap to avoid race condition when multiple REST request comes in parallel.

My query 1- Do i need to make MetricStore variable store volatile? to improve the visibility among multiple requests. 2- I am using Map as the base class and ConcurrentHashMap as Implemetnation, does it affect as Map is not ThreadSafe. -

@Component
class MetricStore{
    public Map<String, Metric> store = new ConcurrentHashMap<>();
    //OR  public volatile Map<String, Metric> store = new ConcurrentHashMap<>();
}

@RestController
class MetricController{
    @Autowired
    private MetricStore metricStore;

    @PostMapping(name="put")
    public void putData(String key, Metric metricData) {
        if(metricStore.store.containsKey(key)) {
            // udpate data
        }
        else {
            metricStore.store.put(key, metricData);
        }
    }

    @PostMapping(name="remove")
    public void removeData(String key) {
        if(metricStore.store.containsKey(key)) {
            metricStore.store.remove(key);
        }
    }

}

Upvotes: 1

Views: 338

Answers (1)

Jacob G.
Jacob G.

Reputation: 29710

Do i need to make MetricStore variable store volatile?

No, because you are not changing the value of store (i.e. store can be marked as final and the code should still compile).

I am using Map as the base class and ConcurrentHashMap as Implemetnation, does it affect as Map is not ThreadSafe

Because you're using a ConcurrentHashMap as the implementation of Map, it is thread-safe. If you want the declared type to be more specific, Map can be changed to ConcurrentMap.


The bigger issue here is that you're using containsKey before calling put and remove when you should be using compute and computeIfPresent, which are atomic operations:

@PostMapping(name="put")
public void putData(String key, Metric metricData) {
    metricStore.store.compute(key, (k, v) -> {
        if (v == null) {
            return metricData;
        }

        // update data
    });
}

@PostMapping(name="remove")
public void removeData(String key) {
    metricStore.store.computeIfPresent(key, (k, v) -> null);
}

Upvotes: 3

Related Questions