Java Concurrent Hashmap initTable () Dlaczego blok try / Final?

Aug 29 2020

Patrzyłem na następujący kod (pochodzący z tego miejsca ) '

/**
 * 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;
}

Czy ktoś może wyjaśnić, dlaczego potrzebny jest blok próbny?

Odpowiedzi

3 rzwitserloot Aug 29 2020 at 20:31

niezaznaczone wyjątki i błędy istnieją jako pojęcie. Jeśli wywołasz .put()ConcurrentHashMap i masz naprawdę mało pamięci, ta mapa może próbować utworzyć tablicę, a to wywołanie może zakończyć się niepowodzeniem z OutOfMemoryError. Kod nadal będzie kontynuowany (być może w bloku catch, który to przechwytuje), a odniesienia do tej mapy nadal istnieją. Byłoby trochę gówno, gdyby potem mapa uległa awarii i została całkowicie unieważniona, ponieważ sizeCtl ma zepsutą wartość.

Chodzi o to, że w końcu blok, który przywraca sizeCtlwartość. Jest to używane do różnych rzeczy, w tym do zarządzania wątkiem, który ma uzyskać dostęp. Gdyby tak się nie stało, wszelkie inne sprawdzenia opcji put obróciłyby się w nieskończoność.

Znaczenie tego (tak jak w przypadku tego, jak często zdarza się rzucanie w tym miejscu, w porównaniu z usunięciem `` prób '' i `` ostatecznie '' i po prostu zakończeniem sizeCtl = sc;bez dodatkowego narzutu prób / wreszcie) jest niskie, ale jeśli jest istotne, to jest całkiem istotne.

Den-Jason Aug 29 2020 at 20:32

To rodzaj muteksu - i podwójnej blokady.

po pierwsze

if ((sc = sizeCtl) < 0)
    Thread.yield(); // lost initialization race; just spin

sprawdza sizeCtlmutex. Jeśli jest mniej niż zero, ktoś inny go złapał, więc po prostu poczekamy chwilę.

Jeśli wynosi zero (lub więcej), są szanse, że uzyskamy zamek. Tak więc przeprowadzana jest operacja „Porównaj i zamień”:

if (U.compareAndSetInt(this, SIZECTL, sc, -1)

To jest operacja atomowa - jeśli inny wątek chwyciłby ją między pierwszym a drugim sprawdzeniem, ifnie zostałby pobrany.

Jeśli CAS zakończy się powodzeniem, metoda staje się odpowiedzialna za zwolnienie muteksu. W związku z tym a try-finallysłuży do zapewnienia, że ​​mutex zostanie zwolniony ( sizeCtlzresetowany do jego pierwotnej wartości) niezależnie od tego, czy wystąpi wyjątek (np. Z powodu braku pamięci przypisanie nowego węzła - lub jeśli istnieje unikalne ograniczenie mające zastosowanie do mapy), czy nie.