Co oznacza „atomowy” w programowaniu?

274

W książce Effective Java napisano:

Specyfikacja języka gwarantuje, że odczyt lub zapis zmiennej jest niepodzielny, chyba że zmienna jest typu longlub double[JLS, 17.4.7].

Co oznacza „atomowy” w kontekście programowania Java lub programowania w ogóle?

James
źródło
24
Jedna operacja na raz.
Subhrajyoti Majumder
1
na raz można wykonać tylko jedną operację na zmiennej.
kaysush
1
podejrzewam, że pytania dotyczące filozofii znajdują się w codereview.stackexchange.com
Phlip
Zauważając, że niektóre zmienne domyślnie nie mają atomowego odczytu i zapisu, deklarując je jako volatile longlub volatile doubleczyni atomowym zapis i atomowym.
H2ONaCl

Odpowiedzi:

372

Oto przykład, ponieważ przykład jest często jaśniejszy niż długie wyjaśnienie. Załóżmy, że foojest zmienną typu long. Następująca operacja nie jest operacją atomową:

foo = 65465498L;

Rzeczywiście, zmienna jest zapisywana za pomocą dwóch oddzielnych operacji: jednej, która zapisuje pierwsze 32 bity, i drugiej, która zapisuje ostatnie 32 bity. Oznacza to, że inny wątek może odczytać wartość fooi zobaczyć stan pośredni.

Wykonanie operacji atomowej polega na użyciu mechanizmów synchronizacji, aby upewnić się, że operacja jest postrzegana, z dowolnego innego wątku, jako pojedyncza, atomowa (tj. Nierozdzielna na części) operacja. Oznacza to, że każdy inny wątek, po wykonaniu operacji atomowej, zobaczy albo wartośćfoo przed przypisaniem, albo po przypisaniu. Ale nigdy wartość pośrednia.

Prostym sposobem na to jest uczynienie zmiennej zmienną :

private volatile long foo;

Lub zsynchronizuj każdy dostęp do zmiennej:

public synchronized void setFoo(long value) {
    this.foo = value;
}

public synchronized long getFoo() {
    return this.foo;
}
// no other use of foo outside of these two methods, unless also synchronized

Lub zastąpić go AtomicLong:

private AtomicLong foo;
JB Nizet
źródło
75
Zakłada się, że działa on w systemie 32-bitowym. Co jeśli byłby to system 64-bitowy? Czy foo = 65465498L; być atomowym?
Harke
46
@Harke Jeśli używasz 64-bitowej Java, tak.
Jeroen
4
Czy dotyczy to również C # i .NET? Jeśli tak, aby foo uzyskało zachowanie atomowe, CLR musi być 64-bitowy?
Fabiano
5
@Fabiano Ma zastosowanie i oto jak to osiągnąć w .NET, ponieważ nie mamy zsynchronizowanego słowa kluczowego takiego jak Java. stackoverflow.com/questions/541194/…
The Muffin Man
2
Załóżmy zatem, że wątek A przypisuje długą wartość, a następnie w połowie wątek B próbuje go odczytać. Jeśli operacja A jest atomowa, to wątek B będzie czekał na zakończenie? Czy to oznacza, że ​​operacje atomowe zapewnią ukryte bezpieczeństwo wątków?
Teoman shipahi
60

„Operacja atomowa” oznacza operację, która wydaje się być natychmiastowa z perspektywy wszystkich innych wątków. Kiedy obowiązuje gwarancja, nie musisz się martwić o częściowo zakończoną operację.

H2ONaCl
źródło
25

Jest to coś, co „wydaje się reszcie systemu występować natychmiast” i mieści się w kategoryzacji liniowości w procesach obliczeniowych. Cytując dalej ten linkowany artykuł:

Atomowość jest gwarancją izolacji od współbieżnych procesów. Ponadto operacje atomowe zwykle mają definicję powodzenia lub niepowodzenia - albo skutecznie zmieniają stan systemu, albo nie mają widocznego efektu.

Na przykład w kontekście systemu bazy danych można mieć „zatwierdzenia atomowe”, co oznacza, że ​​można przesłać zestaw zmian do relacyjnej bazy danych, a wszystkie zmiany zostaną albo przesłane, albo żadna z nich w ogóle w przypadku awarii, w ten sposób dane nie ulegają uszkodzeniu, aw następstwie blokad i / lub kolejek, następną operacją będzie inny zapis lub odczyt, ale dopiero po fakcie. W kontekście zmiennych i wątków jest tak samo w przypadku pamięci.

W cytacie podkreślono, że nie należy oczekiwać takiego zachowania we wszystkich przypadkach.

Grant Thomas
źródło
15

Właśnie znalazłem post Operacje atomowe vs. nieatomowe, które są dla mnie bardzo pomocne.

„Operacja działająca na pamięci współużytkowanej jest atomowa, jeśli kończy się w jednym kroku względem innych wątków.

Gdy magazyn atomowy jest wykonywany w pamięci współdzielonej, żaden inny wątek nie może zaobserwować modyfikacji do połowy.

Kiedy obciążenie atomowe jest wykonywane na wspólnej zmiennej, odczytuje całą wartość taką, jaka pojawiła się w danym momencie ”.

Kurt Zhong
źródło
14

Jeśli masz kilka wątków wykonujących metody m1 i m2 w kodzie poniżej:

class SomeClass {
    private int i = 0;

    public void m1() { i = 5; }
    public int m2() { return i; }
}

masz gwarancję, że każde wywołanie wątku m2będzie miało wartość 0 lub 5.

Z drugiej strony za pomocą tego kodu (gdzie ijest długi):

class SomeClass {
    private long i = 0;

    public void m1() { i = 1234567890L; }
    public long m2() { return i; }
}

wywołanie wątku m2może odczytać 0, 1234567890L lub inną losową wartość, ponieważ i = 1234567890Lnie można zagwarantować, że instrukcja jest niepodzielna dla a long(maszyna JVM może zapisać pierwsze 32 bity i ostatnie 32 bity w dwóch operacjach, a wątek może zaobserwować ipomiędzy nimi) .

assylias
źródło
jak myślisz, dlaczego „długi” powoduje problem, a „int” nie? Proszę zobaczyć tutaj geekswithblogs.net/BlackRabbitCoder/archive/2012/08/09/...
onmyway133
1
Długie i podwójne przypisania @ centropy nie są gwarantowane w Javie jako atomowe. Więc możesz przeczytać długi, w którym tylko połowa bitów została zaktualizowana po przypisaniu.
assylias
0

W Javie pola do odczytu i zapisu wszystkich typów oprócz długiego i podwójnego występują atomowo, a jeśli pole jest zadeklarowane zmiennym modyfikatorem, nawet długie i podwójne są atomowo odczytywane i zapisywane. Oznacza to, że otrzymujemy 100% tego, co tam było lub co się tam wydarzyło, ani nie może być żadnego pośredniego wyniku w zmiennych.

Eugene Shamkin
źródło