Как можно выполнить условия (sc == rs + 1 || sc == rs + MAX_RESIZERS) в функции addCount ConcurrentHashMap (JDK1.8 или более поздней версии) - PullRequest
0 голосов
/ 27 ноября 2018

Я прочитал исходный код функции addCount в ConcurrentHashMap, но не понимаю, когда могут быть выполнены условия ( sc == rs + 1 || sc == rs + MAX_RESIZERS).

почему бы не использовать sc == ( rs<<<RESIZE_STAMP_SHIFT ) +1 || sc == ( rs<<<RESIZE_STAMP_SHIFT ) + MAX_RESIZERS

В функции addCount(long x, int check) ConcurrentHashMap (JDK1.8 или более поздней версии) есть некоторый код, следующий как

if(check >=0{
    Node<K, V>[] tab, nt;
    int n, sc;
    while (s >= (long) (sc = sizeCtl) && (tab = table) != null &&
            (n = tab.length) < MAXIMUM_CAPACITY) {
        int rs = resizeStamp(n);

        if (sc < 0) {
            // the problem is here : 
            // the condition sc == rs + 1 ||  sc == rs + MAX_RESIZERS
            // seems always to be false
            if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                    sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
                    transferIndex <= 0)

                break;
            if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
                transfer(tab, nt);
        } else if (U.compareAndSwapInt(this, SIZECTL, sc,
                (rs << RESIZE_STAMP_SHIFT) + 2))
            transfer(tab, null);
        s = sumCount();
    }
}

Я проделал некоторую работу по восстановлению, чтобы понять, как работает resizeStamp (n) и как работает переменная sizeCtl.

Таким образом, когда один поток впервые получает возможность изменить размер массива сегментов в ConcurrentHashMap, он выполнит операцию

U.compareAndSwapInt(this, SIZECTL, sc,(rs << RESIZE_STAMP_SHIFT) + 2)

, чтобы sizeCtl сталотрицательное значение, которое может обозначать, что некоторый поток делает изменение размера в массиве сегмента.

, когда sizeCtl становится отрицательным, младший 16 бит содержит информацию о том, сколько потоков выполняет одновременное изменение размера.

Вот мои мысли:

, поскольку переменная sc является локальной переменной типа int, как показывает код int n, sc;

после выполнения s >= (long) (sc = sizeCtl),

значение sc никогда не будет изменено для theard за одну итерацию.

Я перечислил весь код snnippet, у которого есть шанс изменить sizeCtl за один цикл, пока цикл

Два элемента в функции addCount:

  • else if (U.compareAndSwapInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2))

    • цель этого - попытаться бороться за возможность быть первым потоком, который изменяет размеры
  • if ( U.compareAndSwapInt(this, SIZECTL, sc, sc + 1) )

    • цель этого - попытаться бороться за возможность помочь изменить размер

Три части в transfer функция

  • sizeCtl = Integer.MAX_VALUE;

    • это справиться с ошибкой ООП
  • sizeCtl = (n << 1) - (n >>> 1);

    • это произойдет, когда все элементы в старой таблице будут перенесены в новую таблицу, последний поток, который не вернулся из transfer, установит sizeCtl для следующего порога, то есть 0.75 * (2n), обратите внимание, n является старым значением.2n - это новая емкость
  • U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)

    • - это управление размером номера потока, которое может определить, какая нить собираетсявозврат из перевода

Согласно всей вышеупомянутой информации, я нашел:

После ввода ветки if if (sc < 0), что означает, что sizeCtl естьбыл назначен (rs << RESIZE_STAMP_SHIFT) + 2, sc shuold быть "большим" отрицательным числом с 16 старшими битами, вычисленными из resizeStamp(n).Условия

   sc == rs + 1 
 ||sc == rs + MAX_RESIZERS 

никогда не могут быть достигнуты, учитывая, что

  • MAX_RESIZERS равно 65535

  • .максимальное значение rs равно Integer.numberOfLeadingZeros(MAXIMUM_CAPACITY ) | (1 << (RESIZE_STAMP_BITS - 1)), что равно 32769

Я думаю, что следующие условия имеют больше смысла

sc == ( rs<<<RESIZE_STAMP_SHIFT ) +1, чтобы судить о том, что все потоки завершили изменение размера

sc == ( rs<<<RESIZE_STAMP_SHIFT ) + MAX_RESIZERS, чтобы судить, достиг ли размер резьбы уже максимального предела MAX_RESIZERS.

Может ли кто-нибудь помочь мне объяснить, если я ошибаюсь, большое спасибо!

Следующим шагом является небольшой эксперимент, который я провел, чтобы проверить свою мысль

Спасибо за предложение от Карлоса Хойбергера

Можете ли вы опубликовать минимальный, полный и проверяемый пример, чтобы продемонстрировать проблему?

  • Сначала скопируйте ConcurrentHashMap исходный код для вашего собственного пакета
  • Во-вторых, сделайте некоторые необходимые изменения, чтобы его можно было скомпилировать (например: изменить объявление пакета, заставить работать небезопасный экземпляр, скопировать ThreadLocalRandom в ваш пакет, поскольку используется ConcurrentHashMapФункция ThreadLocalRandom.probe (), которая не является общедоступной)
  • В-третьих, уменьшите MAX_RESIZERS до 2, как показано в документации, этот должен убедиться, что могут выполняться не более 2 потоков.одновременное изменение размера

    private static final int MAX_RESIZERS = 2;

  • В-четвертых, добавьте следующий фрагмент кода в настроенный класс ConcurrentHashMap

    public static void main(String[] args) {
    
    ConcurrentHashMap hashMap = new ConcurrentHashMap(8);
    
    for(int i = 0; i< 300; i++)
    {
        new Thread() {
            @Override
            public void run() {
                hashMap.put(Thread.currentThread().getId(),"id: "+Thread.currentThread().getId());
            }
        }.start();
    }
    

    }

  • В-пятых, добавьте следующий фрагмент кода в функцию transfer ConcurrentHashMap.Чтобы приостановить любой поток, введенный в transfer

        if (nextTab == null) {            // initiating
        try {
            @SuppressWarnings("unchecked")
            Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
            nextTab = nt;
        } catch (Throwable ex) {      // try to cope with OOME
            sizeCtl = Integer.MAX_VALUE;
            return;
        }
        nextTable = nextTab;
        transferIndex = n;
    }
    //  The following added code here is to suspend Threads !!!!
    try {
        String s = new String();
        synchronized (s)
        {
            s.wait();
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    
  • Шесть, добавьте точку разрыва потока в следующей строке кода в addCount function

    • (Совет: я использовал Idea Intellij, выберите опцию «Поток», чтобы приостановить каждый поток в вашем приложении, в противном случае будет приостановлен только первый поток, выполненный до точки останова)

                  if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
                  transfer(tab, nt);
      

      enter image description here

  • Затем запустите основную функцию, вы увидите более чем 2 введенных функции transfer, что означает, что MAX_RESIZERS не принимает никакихэффект.

1 Ответ

0 голосов
/ 28 ноября 2018

Я отправил этот вопрос как отчет об ошибке в Oracle.Он прошел оценку и стал видимым в базе данных ошибок jdk с идентификатором ошибки: JDK-8214427

Вот ссылка на отчет об ошибке: BUG: JDK-8214427 .Обратите внимание, что метод исправления, указанный в отчете об ошибке, неверен из-за моей ошибки

Резюме:

Условия

( sc == rs + 1 || sc == rs + MAX_RESIZERS)

следует изменить на

sc  ==  ( rs<<<RESIZE_STAMP_SHIFT ) +1 || sc  ==  ( rs<<<RESIZE_STAMP_SHIFT ) + MAX_RESIZERS

Исправленный код JDK-12 теперь доступен здесь

 if (check >= 0) {
            Node<K,V>[] tab, nt; int n, sc;
            while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
                   (n = tab.length) < MAXIMUM_CAPACITY) {
                int rs = resizeStamp(n) << RESIZE_STAMP_SHIFT;
                if (sc < 0) {
                    if (sc == rs + MAX_RESIZERS || sc == rs + 1 ||
                        (nt = nextTable) == null || transferIndex <= 0)
                        break;
                    if (U.compareAndSetInt(this, SIZECTL, sc, sc + 1))
                        transfer(tab, nt);
                }
                else if (U.compareAndSetInt(this, SIZECTL, sc, rs + 2))
                    transfer(tab, null);
                s = sumCount();
            }
        }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...