How can I perform a thread-safe get then remove with ConcurrentHashMap?

13,552

Solution 1

You are correct. If this Map can be modified by multiple threads, it's possible that the first call to chm.get(key) will return a non-null value and the second call will return null (due to the removal of the key from the Map done by another thread), and thus chm.get(key).doSomething() will throw a NullPointerException.

You can make this code thread safe by using the local variable to store the result of chm.get(key):

ConcurrentHashMap<Integer, Integer> chm = new ConcurrentHashMap<Integer, Integer>();
Integer value = chm.get(key);

if(value != null) {
    value.doSomething(); // P.S. Integer class doesn't have a doSomething() method
                         // but I guess this is just an example of calling some arbitrary 
                         // instance method
    chm.remove(key);
}

BTW, even if that Map wasn't a ConcurentHashMap and only one thread had access to it, I'd still use a local variable, since it's more efficient than calling the get() method twice.

EDIT :

As commented below, this fix will not prevent doSomething() from being called multiple times for the same key/value by different threads. Whether or not that's the desired behavior is not clear.

If you wish to prevent the possiblity of doSomething() being called by multiple threads for the same key/value, you can use chm.remove(key) for both removing the key and obtaining the value at the same step.

This however runs the risk that doSomething() will not be executed at all for some key/value, since if the first call to doSomething() resulted in an exception, there won't be another call to doSomething() by another thread, since the key/value pair will no longer be in the Map. On the other hand, if you remove the key/value pair from the Map only after doSomething() executes succesfully, you guarantee that doSomething() is executed successfuly at least once for all the key/value pairs that were reomved from the Map.

Solution 2

Map.remove(key) returns the value if it has been removed. This is a very good trick in many situations, including yours:

Object value = chm.remove(key)
if(value != null)
{
     value.doSomething();
}

You can't work safely with a get then a remove because if two threads call your method at the same time, there's always a risk they call doSomething two or more times, before the key has been removed.

This is not possible if you remove it first. The code above is Threadsafe, also simpler.

Share:
13,552
Rohit Manglik
Author by

Rohit Manglik

EduGorilla’s CEO, Rohit Manglik harbors the yearning to revamp the ‘chalk-n-talk’ system of education and patch it with modern AI &amp; VR powered digital learning aids. He believes that smart technologies make learning easier, faster and more interactive. And his venture EduGorilla Community Pvt. Ltd. falls in line with the same belief. Rohit graduated from NIT-Surathkal and was quite satisfied with his life. He had everything that one could ask for, a loving family, a handsomely paying job and a bunch of friends to lift his sunken spirits. But, it takes just one day, one moment and one event to change your life completely. And that day arrived in Rohit’s life a few years back when he visited his alma mater in Farrukhabad. He saw the dropped faces of his friends, listened to their issues and realized that they are not getting the high-end privileges that they deserve. It was then that he realized that some problems are so deep-rooted in the education sector of India that they cannot be changed with ease. However, Rohit Manglik is not one of those who sit comfortably on their couches and discuss the issues prevalent in society. Rather he believes in doing something for it and that is what he did. Rohit came up with a revolutionary idea to bridge the gap between educationists and students and foster a healthy dialogue among all stakeholders of education. And that is how EduGorilla came into existence. Founded in March 2016, EduGorilla is the largest EdTech startup of Uttar Pradesh and the second largest of India. It has architected a good repute among the students seeking answers to career-related issues and seeking judicious guidance. EduGorilla offers digitized course content for various competitive exams like IIT, CAT, NEET, UPSC and has captured $2.4 billion from the Indian EdTech market. It has around 200000 active subscribers, 1600000 monthly visitors, and a month-per-month growth rate of 25%. However, this journey was not easy. The leading edupreneur of Lucknow had to wait for 8-9 months to get his first seed of investment. Those were the days of hardships and struggles. He used to travel day and night pitching his idea to the investors and had even slept on railway stations. But what kept him going through the ups and downs was his devotion towards his dream. Gradually, things fell into place, his efforts paid back, and his dream venture got an identity.

Updated on June 19, 2022

Comments

  • Rohit Manglik
    Rohit Manglik almost 2 years

    In an interview, I was asked to check whether following code works as intended.

    ConcurrentHashMap<Integer, Integer> chm = new ConcurrentHashMap<>();
    
    if (chm.get(key) != null) {
        chm.get(key).doSomething();
        chm.remove(key);
    }
    

    According to JavaDocs, get returns value of last completed update operation. So if thread 1 already called chm.remove(key) and if thread 2 came inside the if statement and is about to call get method then we might get an exception. Is this correct?

    How can I make this thread-safe?

  • Erick G. Hagstrom
    Erick G. Hagstrom over 8 years
    Probably be even better to just write Integer value = chm.remove(key);, and remove the existing chm.remove(key); line entirely. That way the value isn't lying around in the map while you're invoking doSomething().
  • Eran
    Eran over 8 years
    @ErickG.Hagstrom I don't know if it's a problem that the value is lying around in the map while you're invoking doSomething(). Perhaps the requirement is to remove the key/value entry from the Map only after doSomething is executed successfully.
  • Guillaume F.
    Guillaume F. over 8 years
    This is not Threadsafe, doSomething can be called more than once.
  • JB Nizet
    JB Nizet over 8 years
    @GuillaumeF. and that can be desired or not. We don't know.
  • Eran
    Eran over 8 years
    @GuillaumeF. Yes, doSomething may be called more than once, but it's not necessarily a problem. My code fixes the exception that the original snippet may cause. Whether or not doSomething should be prevented from being called twice for the same key/value depends on the specific use case.
  • Tamas Hegedus
    Tamas Hegedus over 8 years
    Somehow I feel like the interviewer would expect this as a right answer