Java Concurrent Hashmap initTable () Почему блокировка try / finally?
Я смотрел следующий код ( отсюда ) '
/**
* Initializes table, using the size recorded in sizeCtl.
*/
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;
}
Может кто-нибудь объяснить, зачем нужен блок try?
Ответы
непроверенные исключения и ошибки существуют как концепция. Если вы вызываете .put()ConcurrentHashMap и у вас очень мало памяти, эта карта может попытаться создать массив, и этот вызов может завершиться ошибкой OutOfMemoryError. Код по-прежнему будет продолжаться (возможно, в блоке catch, который это улавливает), и ссылки на эту карту все еще существуют. Было бы немного неприятно, если после этого карта выйдет из строя и станет полностью недействительной, потому что sizeCtl имеет неверное значение.
Дело в том, что блок finally, восстанавливающий sizeCtlзначение. Это используется для разных вещей, в том числе для управления тем, какой поток получает доступ. В противном случае любые другие пут-коллы вращались бы вечно.
Актуальность этого (например, как часто здесь происходит бросок, по сравнению с удалением «try» и «finally» и просто завершением sizeCtl = sc;без дополнительных накладных расходов на try / finally) невысока, но если это актуально, это вполне актуально.
Это своего рода мьютекс и блокировка с двойной проверкой.
во-первых
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
проверяет sizeCtlмьютекс. Если он меньше нуля, это значит, что кто-то другой его схватил, поэтому мы просто немного подождем.
Если он равен нулю (или больше), скорее всего, мы получим блокировку. Итак, выполняется сравнение и замена:
if (U.compareAndSetInt(this, SIZECTL, sc, -1)
Это атомарная операция - если другой поток захватит ее между первой и второй проверками, операция ifне будет выполнена.
Если CAS успешно, тогда метод становится ответственным за освобождение мьютекса. Следовательно, a try-finallyиспользуется для обеспечения того, чтобы мьютекс был освобожден ( sizeCtlсброшен в исходное значение) независимо от того, возникает ли исключение (например, из-за нехватки памяти, назначающей новый узел, или если есть уникальное ограничение, применимое к карте) или нет.