2015-05-06 8 views

Odpowiedz

6

Klasy AtomicXXX reprezentują typ danych atomowych. Oznacza to, że muszą zwracać spójne wyniki, gdy dostęp do nich jest współbieżny przez co najmniej dwa wątki. compareAndSet jest operacją, która jest zwykle implementowana bezpośrednio w sprzęcie, więc getAndSet jest zaimplementowana pod kątem compareAndSet.

Metoda działa w następujący sposób: najpierw zwracana jest aktualna wartość. Teraz może być możliwe, że inny wątek jednocześnie zmienia wartość, więc należy sprawdzić, używając compareAndSet, że tak nie jest. Jeśli inny wątek zmienił wartość, procedurę należy powtórzyć, ponieważ w przeciwnym razie zwracana jest błędna wartość. Stąd pętla.

+0

Czy compareAndSet zapewnia jakiekolwiek gwarancje przeciwko głodowi? – Random832

4

W jakim celu pętla jest używana w tym kodzie

Łatwo jest zrozumieć, dlaczego dla pętli jest tam, patrząc co mogłoby się stać, gdyby go nie było.

Załóżmy, że metoda wyglądał następująco:

int current = get(); 
compareAndSet(current, newValue); 
return current; 

Teraz, jeśli inny wątek przyszedł i nazywany getAndSet równocześnie, to może zmienić wartość między

int current = get(); 

i

compareAndSet(current, newValue); 

The compareAndSet zawiedzie, a metoda nie będzie działać tak jak zamierzony.

W związku z powyższym nie jest to jedyny poprawny sposób wdrożenia tej metody. Zakładam, że jest zaimplementowany tak, jak wynika to z wydajności. Zamiast nabywania/zwalniania blokady dla tej operacji, deleguje ona do compareAndSet, która jest prawdopodobnie zaimplementowana z pewną wydajną operacją CAS.

7

Istnieje szkoła myśli, która stwierdza, że ​​powinieneś use locks as frugally as you can. To znaczy. nigdy nie używaj zamka, jeśli możesz go ominąć, a jeśli musisz go użyć, zablokuj go na minimalny czas. Rozumowanie tego wynika z niekiedy znacznych kosztów zabrania blokady w pierwszej kolejności wraz z kosztem jednego wątku oczekującego, podczas gdy inny blokuje zasób, którego potrzebuje.

Nastąpił dostępne dla very long time, instrukcje CPU o nazwie Porównaj i ustawiona (lub CAS w skrócie) zaprojektowany, aby pomóc z tym, że w zasadzie zrobić:

if (value == providedValue) { 
    value = newValue; 
    return true; 
} else { 
    return false; 
} 

instrukcje te można wykonać na maszynie -kod poziomu i są znacznie szybsze niż tworzenie blokady.

Wyobraź sobie, że chcesz dodać 1 do numeru za pomocą jednej z tych instrukcji w sposób, który będzie konsekwentnie działał poprawnie pod dużym obciążeniem równoległym.Wyraźnie można zakodować go jako:

int old = value; 
if (compareAndSet(old, old+1)) { 
    // It worked! 
} else { 
    // Some other thread incremented it before I got there. 
} 

Ale co możemy zrobić, jeśli CAS powiodło? Zgadłeś - spróbuj jeszcze raz!

boolean succeeded = false; 
do { 
    int old = value; 
    if (compareAndSet(old, old+1)) { 
    // It worked! 
    succeeded = true; 
    } else { 
    // Some other thread incremented it before I got there. Just try again. 
    } 
} while (!succeeded); 

I tam widzisz wzór, który obserwujesz.

Posługując się tym i podobnymi idiomami można zaimplementować wiele funkcji, a nawet niektóre dość skomplikowane struktury danych bez użycia blokad (powszechnie zwane Zablokuj bezpłatny). Na przykład: here to implementacja bez blokady bez zabezpieczenia Ring Buffer.

Powiązane problemy