Java Concurrent Hashmap initTable () Perché il blocco try / latest?
Ho esaminato il seguente codice (proveniente da qui ) '
/**
* 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;
}
Qualcuno può spiegare perché è necessario il blocco try?
Risposte
come concetto esistono eccezioni ed errori non controllati. Se chiami .put()un ConcurrentHashMap e sei veramente a corto di memoria, quella mappa potrebbe provare a creare un array e quella chiamata potrebbe fallire con un OutOfMemoryError. Il codice continuerà ancora (al blocco catch che cattura questo, forse) e i riferimenti a quella mappa esistono ancora. Sarebbe un po 'una schifezza se, dopo ciò, la mappa si blocca e viene completamente invalidata perché sizeCtl ha un valore non funzionante.
Il punto è quel blocco finale, che ripristina il sizeCtlvalore. Viene utilizzato per varie cose, tra cui la gestione di quale thread ottiene l'accesso. Se non lo facesse, qualsiasi altra chiamata put girerebbe per sempre.
La rilevanza di questo (come in, quanto spesso si verifica un oggetto lanciabile qui, rispetto alla rimozione del "tentativo" e del "finalmente" e alla fine sizeCtl = sc;senza l'overhead aggiuntivo di provare / infine) è bassa, ma se è rilevante, è abbastanza rilevante.
È una specie di mutex e una serratura a doppio controllo.
In primo luogo
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
controlla il sizeCtlmutex. Se è inferiore a zero, qualcun altro lo ha afferrato, quindi aspettiamo solo un po '.
Se è zero (o più), è probabile che otteniamo il blocco. Quindi viene eseguito un confronto e scambio:
if (U.compareAndSetInt(this, SIZECTL, sc, -1)
Questa è un'operazione atomica: se un altro thread lo afferrasse tra il primo e il secondo controllo, ifnon verrebbe eseguito.
Se il CAS ha esito positivo, il metodo diventa responsabile del rilascio del mutex. Pertanto, a try-finallyviene utilizzato per garantire che il mutex venga rilasciato ( sizeCtlripristinato al suo valore originale) indipendentemente dal fatto che si verifichi un'eccezione (ad esempio memoria insufficiente che assegna il nuovo nodo - o se esiste una restrizione univoca applicabile alla mappa) o meno.