Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Correctly Updating Values in Java ConcurrentHashMap

Tech May 10 4

package study.base.types.map;

import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger;

public class ScoreUpdater implements Runnable {

private AtomicInteger completionCounter;
private ConcurrentHashMap<string integer=""> scoreMap;

public ScoreUpdater(ConcurrentHashMap<string integer=""> map, AtomicInteger counter) {
    this.scoreMap = map;
    this.completionCounter = counter;
}

@Override
public void run() {
    String threadName = Thread.currentThread().getName();
    synchronized (scoreMap) {
        Integer currentValue = scoreMap.get("score");
        Integer updatedValue = currentValue + 1;
        System.out.println(threadName + " :" + updatedValue);
        scoreMap.replace("score", currentValue, updatedValue);
        completionCounter.incrementAndGet();
    }
    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

public static void main(String[] args) throws InterruptedException {
    int threadCount = 33;

    ConcurrentHashMap<string integer=""> scoreMap = new ConcurrentHashMap<>(1, 1);
    scoreMap.put("score", 0);

    AtomicInteger counter = new AtomicInteger(0);
    ScoreUpdater task = new ScoreUpdater(scoreMap, counter);

    List<thread> threads = new ArrayList<>();
    for (int i = 0; i < threadCount; i++) {
        Thread t = new Thread(task, String.valueOf(i));
        threads.add(t);
    }

    for (int i = 0; i < threadCount; i++) {
        threads.get(i).start();
    }

    while (counter.intValue() < threadCount) {
        // busy wait
    }

    System.out.println("Final score: " + scoreMap.get("score"));
}

}


The increment logic retrieves the value, adds one, and puts it back. The result is correct because of the synchronized block. Without synchronization, the result would be unpredictable. However, using synchronized this way defeats the purpose of a concurrent map. A better approach would be: **Original approach:**```

synchronized (scoreMap) {
    Integer currentValue = scoreMap.get("score");
    Integer updatedValue = currentValue + 1;
    scoreMap.replace("score", currentValue, updatedValue);
    completionCounter.incrementAndGet();
}

Improved approach:```

scoreMap.replace("score", scoreMap.get("score") + 1);


Note: The simplified code above works for this specific scenario because the replace method has built-in atomicity. Why does this single line work? Let's examine the replace method in ConcurrentHashMap: ```

public V replace(K key, V value) {
    if (key == null || value == null)
        throw new NullPointerException();
    return replaceNode(key, value, null);
}

final V replaceNode(Object key, V value, Object cv) {
    int hash = spread(key.hashCode());
    for (Node<k>[] tab = table;;) {
        Node<k> f; int n, i, fh;
        if (tab == null || (n = tab.length) == 0 ||
            (f = tabAt(tab, i = (n - 1) & hash)) == null)
            break;
        else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
        else {
            V oldVal = null;
            boolean validated = false;
            synchronized (f) {
                if (tabAt(tab, i) == f) {
                    if (fh >= 0) {
                        validated = true;
                        for (Node<k> e = f, pred = null;;) {
                            K ek;
                            if (e.hash == hash &&
                                ((ek = e.key) == key ||
                                 (ek != null && key.equals(ek)))) {
                                V ev = e.val;
                                if (cv == null || cv == ev ||
                                    (ev != null && cv.equals(ev))) {
                                    oldVal = ev;
                                    if (value != null)
                                        e.val = value;
                                    else if (pred != null)
                                        pred.next = e.next;
                                    else
                                        setTabAt(tab, i, e.next);
                                }
                                break;
                            }
                            pred = e;
                            if ((e = e.next) == null)
                                break;
                        }
                    }
                    // TreeBin handling omitted for brevity
                }
            }
            if (validated) {
                if (oldVal != null) {
                    if (value == null)
                        addCount(-1L, -1);
                    return oldVal;
                }
                break;
            }
        }
    }
    return null;
}
</k></k></k>

The key takeaway is the synchronized (f) block within the implementasion—ConcurrentHashMap's methods have built-in synchronization for certain operations. When learning concurrency, you don't need to dive deep into every detail. Focus on these fundamentals: - Time-sharing and multi-core parallelism principles - Core concurrency concepts - Software-level concurrency implementations - Thread creation in Java - Choosing appropriate lock granularity - Thread-safe data structures in Java - Proper use of synchronized keyword - Thorough testing Reading source code for every class isn't always necessary—do so when you have time or when a specific need arises.

Tags: Java

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.