Java Concurrent Hashmap initTable () Warum der try / finally-Block?
Ich habe mir den folgenden Code angesehen (von hier bezogen ) '
/**
* 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;
}
Kann jemand erklären, warum der Try-Block benötigt wird?
Antworten
ungeprüfte Ausnahmen und Fehler existieren als Konzept. Wenn Sie .put()eine ConcurrentHashMap aufrufen und der Arbeitsspeicher sehr knapp ist, versucht diese Map möglicherweise, ein Array zu erstellen, und dieser Aufruf schlägt möglicherweise mit einem OutOfMemoryError fehl. Der Code wird weiterhin fortgesetzt (möglicherweise am Fangblock, der dies abfängt), und die Verweise auf diese Karte sind weiterhin vorhanden. Es wäre ein bisschen beschissen, wenn die Karte danach abstürzt und vollständig ungültig wird, weil sizeCtl einen fehlerhaften Wert hat.
Der Punkt ist der endgültige Block, der den sizeCtlWert wiederherstellt . Dies wird für verschiedene Zwecke verwendet, insbesondere für die Verwaltung, welcher Thread Zugriff erhält. Wenn dies nicht der Fall wäre, würden sich alle anderen Put-Calls für immer drehen.
Die Relevanz davon (wie in, wie oft tritt hier ein Wurf auf, im Vergleich zum Entfernen des 'try' und des 'finally' und nur zum Ende sizeCtl = sc;ohne den zusätzlichen Aufwand von try / finally) ist gering, aber wenn es relevant ist, es ist ziemlich relevant.
Es ist eine Art Mutex - und ein Double-Check-Schloss.
zuerst
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
überprüft den sizeCtlMutex. Wenn es weniger als Null ist, hat es jemand anderes gepackt, also warten wir nur ein bisschen.
Wenn es null (oder mehr) ist, erhalten wir wahrscheinlich die Sperre. So wird ein Compare-And-Swap durchgeführt:
if (U.compareAndSetInt(this, SIZECTL, sc, -1)
Dies ist eine atomare Operation - wenn ein anderer Thread sie zwischen der ersten und zweiten Prüfung packte, wurde die ifnicht genommen.
Wenn das CAS erfolgreich ist, ist die Methode für die Freigabe des Mutex verantwortlich. Daher wird a try-finallyverwendet, um sicherzustellen, dass der Mutex freigegeben wird ( sizeCtlauf seinen ursprünglichen Wert zurückgesetzt), unabhängig davon, ob eine Ausnahme auftritt (z. B. nicht genügend Speicher, um den neuen Knoten zuzuweisen - oder ob für die Karte eine eindeutige Einschränkung gilt) oder nicht.