Java simultâneo Hashmap initTable () Por que o bloco try / finally?
Estive olhando o seguinte código (proveniente daqui ) '
/**
* 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;
}
Alguém pode explicar por que é necessário o bloco try?
Respostas
exceções não verificadas e erros existem como um conceito. Se você chamar .put()um ConcurrentHashMap e estiver realmente com pouca memória, esse mapa pode tentar fazer uma matriz e essa chamada pode falhar com um OutOfMemoryError. O código ainda continuará (no bloco catch que captura isso, talvez), e as referências a esse mapa ainda existem. Seria uma merda se, depois disso, o mapa travasse e fosse completamente invalidado porque sizeCtl tem um valor quebrado.
O ponto é aquele bloco finalmente, que restaura o sizeCtlvalor. Isso é usado para várias coisas, incluindo o gerenciamento de qual thread obtém acesso. Do contrário, quaisquer outras chamadas put girariam para sempre.
A relevância disso (como em, quantas vezes um lançável ocorre aqui, versus remover o 'try' e o 'finally' e apenas terminar com sizeCtl = sc;sem a sobrecarga adicional de try / finally) é baixa, mas se for relevante, é bastante relevante.
É uma espécie de mutex - e um bloqueio de verificação dupla.
primeiramente
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
verifica o sizeCtlmutex. Se for menor que zero, outra pessoa o agarrou, então esperamos um pouco.
Se for zero (ou mais), é provável que obtenhamos o bloqueio. Portanto, uma comparação-e-troca é realizada:
if (U.compareAndSetInt(this, SIZECTL, sc, -1)
Esta é uma operação atômica - se outro encadeamento o agarrasse entre a primeira e a segunda verificação, o ifnão seria obtido.
Se o CAS for bem-sucedido, o método se torna responsável por liberar o mutex. Portanto, a try-finallyé usado para garantir que o mutex seja liberado ( sizeCtlredefinido para seu valor original) independentemente de ocorrer uma exceção (por exemplo, falta de memória atribuindo o novo nó - ou se houver uma restrição exclusiva aplicável ao mapa) ou não.