Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8252364

ConcurrentHashMap.initTable() may init multiple times in concurrent development

XMLWordPrintable

      ADDITIONAL SYSTEM INFORMATION :
      win10, openJDK 11

      A DESCRIPTION OF THE PROBLEM :
      If we use non-default constructor to new a concurrenthashmap which used ConcurrentHashMap(int initialCapacity) to construct an instance.
      I found its inner variable named table might initialized multiple times.
      I think this may occur some inknown problem in runtime because table variable should be singleton always.


      below is my analysis:
      Problem is related to U.compareAndSetInt(this, SIZECTL, sc, -1) //line 2288
      Because we use non-default ctor() to instante one map. So its SIZECTL is not default value(such as 64). If one thread cas SIZECTL successfully and another thread also cas successfully in the second turn with table is still null , while will result in table instantiate twice or multiple times.



      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      step 1: use ConcurrentHashMap(int initialCapacity) to new an instance
      step 2: use 10000+ threads to put value. such as map.put("key","value")


      ---------- BEGIN SOURCE ----------
          private final Node<K,V>[] initTable() {
              Node<K,V>[] tab; int sc;
              while ((tab = table) == null || tab.length == 0) {
                  if ((sc = sizeCtl) < 0)
                      Thread.yield(); // lost initialization race; just spin
                  else if (U.compareAndSetInt(this, SIZECTL, sc, -1)) {
                      try {
                          if ((tab = table) == null || tab.length == 0) {
                              int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                              @SuppressWarnings("unchecked")
                              Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                              table = tab = nt;
                              sc = n - (n >>> 2);
                          }
                      } finally {
                          sizeCtl = sc;
                      }
                      break;
                  }
              }
              return tab;
          }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      add one mutex to lock the branch. Because map initialize table rapidly, it does not take time to park other thread in concurrent runtime.

        private final Node<K,V>[] initTable() {
              Node<K,V>[] tab; int sc;
              while ((tab = table) == null || tab.length == 0) {
                  if ((sc = sizeCtl) < 0)
                      Thread.yield(); // lost initialization race; just spin
                  else if (U.compareAndSetInt(this, SIZECTL, sc, -1)) {
                      try {
                          if ((tab = table) == null || tab.length == 0) {

      synchronized(mutex){
      int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
      @SuppressWarnings("unchecked")
      Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
      table = tab = nt;
      sc = n - (n >>> 2);
      }
                          }
                      } finally {
                          sizeCtl = sc;
                      }
                      break;
                  }
              }
              return tab;
          }

            tongwan Andrew Wang
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: