Я прочитал исходный код функции 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
никогда не могут быть достигнуты, учитывая, что
Я думаю, что следующие условия имеют больше смысла
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);
- Затем запустите основную функцию, вы увидите более чем 2 введенных функции
transfer
, что означает, что MAX_RESIZERS не принимает никакихэффект.