Java Concurrent Hashmap initTable () Pourquoi le bloc try / finally?
J'ai regardé le code suivant (provenant d' ici ) '
/**
* 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;
}
Quelqu'un peut-il expliquer pourquoi le bloc try est nécessaire?
Réponses
des exceptions et des erreurs non contrôlées existent en tant que concept. Si vous appelez .put()un ConcurrentHashMap et que vous êtes vraiment à court de mémoire, cette carte peut essayer de créer un tableau, et cet appel peut échouer avec un OutOfMemoryError. Le code continuera toujours (au bloc catch qui attrape cela, peut-être), et les références à cette carte existent toujours. Ce serait un peu merdique si, après cela, la carte se bloque et est complètement invalidée car sizeCtl a une valeur cassée.
Le point est que finalement le bloc, qui restaure la sizeCtlvaleur. Ceci est utilisé pour diverses choses, notamment la gestion du thread qui a accès. Si ce n'était pas le cas, tous les autres appels mis tourneraient pour toujours.
La pertinence de ceci (comme dans, à quelle fréquence un jetable se produit-il ici, par rapport à la suppression du `` try '' et du `` finalement '' et se terminant simplement par sizeCtl = sc;sans la surcharge supplémentaire de try / finally) est faible, mais si c'est pertinent, c'est tout à fait pertinent.
C'est une sorte de mutex - et un verrou à double contrôle.
d'abord
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
vérifie le sizeCtlmutex. S'il est inférieur à zéro, quelqu'un d'autre l'a attrapé, alors nous attendons juste un peu.
Si c'est zéro (ou plus), il y a de fortes chances que nous obtenions le verrou. Ainsi, un Compare-And-Swap est effectué:
if (U.compareAndSetInt(this, SIZECTL, sc, -1)
Il s'agit d'une opération atomique - si un autre thread le saisissait entre le premier et le deuxième contrôle, le ifne serait pas pris.
Si le CAS réussit, alors la méthode devient responsable de la libération du mutex. Par conséquent, a try-finallyest utilisé pour garantir que le mutex est libéré ( sizeCtlréinitialisé à sa valeur d'origine) indépendamment du fait qu'une exception se produise (par exemple, mémoire insuffisante pour affecter le nouveau nœud - ou s'il existe une restriction unique applicable à la carte) ou non.