A bug in ConcurrentHashMap

Recently found java 1.8's concurrentHashMap, when using computeIfAbsent, if it involves modifying the map, it will generate a bug.
The sample code is as follows:

        System.out.println("start.");
        map.computeIfAbsent("t",
                (String t) -> map.computeIfAbsent("t", (String i) -> "i")); //halt is here
        System.out.println("fin.");

If you execute this code, you will find that the code will stop at the comment and there will be no results.
At first I thought it was a recursive implementation. In layman's terms, it was a self-recursion when constructing a function. That is, you want to construct an A, but the construction of A depends on some properties of A after the construction. To verify if this is the reason, we made some adjustments to the code to eliminate recursive calls.

        ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
        System.out.println("start.");
        map.computeIfAbsent("t",
                (String t) -> {
                    map.put("t", "t");
                    return "t";
                });
        System.out.println("fin.");

You will find that the code continues to stop where you can't output "fin."

Then suspected to be a deadlock, suspected that the concurrentHashMap uses a non-reentrant lock. But with the implementation of the conrrentHashMap, it is found that it is based on cas + synchronized, and the synchronization itself is reentrant, so the deadlock condition is not met here.
Continue to look at the comment for concurrentHashMap, which has a sentence like this:

/*
  must not attempt to update any other mappings of this map.
*/

This sentence confirms that this problem should be known to exist.
So you should definitely avoid recursion in computeIfAbsent, or modify any operation of the map.

In order to clarify the reasons, we continue to debug the source code of the concurrentHashMap, and found that in the computeIfAbsent, if you try to modify the map, the code will be

                For (Node<K,V>[] tab = table;;) { // infinite loop
            Node<K,V> f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();
            else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
                Node<K,V> r = new ReservationNode<K,V>();
                synchronized (r) {
                    if (casTabAt(tab, i, null, r)) { //cas
....

Repeated loops.
I tried to explain this problem in a common way:
Note:Not necessarily correct, just personal understanding
Since the cas operation is used in the concurrentHashMap, in the case of cas nesting, a "deadlock" is formed. For example, a value is originally 1, I want to change it to 2, the normal cas operation will compare the value at the moment of modification, whether the value is still 1. This comparison, in the case of only one layer of cas, is no problem. However, if there are two layers of cas, the value is originally 1, and the first layer is 1 -> 2,When cas is not yet in effect, continue to enter the second layer cas operation, put 2 -> 3, when the final commit, the second layer cas compares the current value is 2, but since the current index is still 1, the modification is invalid. Eventually it enters the loop repeatedly, forming a deadlock.

Although the code comment in computeIfAbsent gives a strong hint to the behavior of this modified map, in practice, I think this behavior is still an implementation bug of concurrentHashMap.

https://bugs.openjdk.java.net/browse/JDK-8172951
Fortunately, this problem has been basically fixed in java 1.9.

This is fixed in JDK 9 with JDK-8071667 . When the test case is run in JDK 9-ea, it gives a ConcurrentModification Exception.
java 9

Intelligent Recommendation

Qt5.15 download and installation tutorial

QT5.15 installation Changes in QT 5.15 Online installer address download 5.15.2 installation Changes in QT 5.15 Official Announcement http://download.qt.io/official_releases/qt/5.15/5.15.0/OFFLINE_REA...

Power of 71, 4

Description of the topic is the first loop to achieve Feeling is also a test of mathematics ability, I don’t understand this code very well....

Teach you to get started with Gephi to make a paper based on the co-occurrence matrix

Introduction: Gephi is an open source free cross-platform JVM-basedComplex network analysis software, mainly used in variousNetwork and complex systemsInteractive visualization and detection of open s...

MUI Framework -02- Notes - Applicable Scenarios - Implementing Value Passing Between Pages

MUI Framework -02- Notes - Applicable Scenarios - Implementing Value Passing Between Pages Regarding development, it doesn't make much sense to copy too much. Please check: Official documentation:http...

VUE filter

VUE filter [Vue Official Document] Vue.js Allows you to customize the filter, can be used for some common text. Filters can be used in two places: double roster interpolation and V-bind expressions (t...

More Recommendation

Js algorithm question -2, two counts added

Articles directory 1. Title 2. Recursive thinking one: I use myself 3. Recursive thought two: Exit 4, recursive thought three: update status 5, all code 1. Title Give two linked lists and seek peace.h...

Database transaction operations

Database transaction operations, take mysql as an example: First enter a database in mysql   After entering the database, you can add, delete, and change the data into operations By default, mysq...

Stored procedure use

Use stored procedures to do bulk operations, such as: batch creation of tables  ...

Js and html combination

1. Use the tag <script> to write the code of js directly in this tag.     <script> alert("good");     </script> 2. Introduce an external js file &n...

Copyright  DMCA © 2018-2026 - All Rights Reserved - www.programmersought.com  User Notice

Top