Що означає "атомний" в програмуванні?


276

У книзі "Ефективна Java" зазначено:

Специфікація мови гарантує, що читання або запис змінної є атомним, якщо змінна не є типом longабо double[JLS, 17.4.7].

Що означає "атомний" в контексті програмування Java, або програмування взагалі?


24
Одна операція за раз.
Subhrajyoti Majumder

1
на змінній можна одночасно виконати лише одну операцію.
Кайшуш


1
я підозрюю, що питання філософії належать у codereview.stackexchange.com
Phlip

Зазначаючи, що деякі змінні за замовчуванням не мають атомних читання і запису, оголошуючи їх як volatile longчи volatile doubleзмушують читати атомні та писати атомні.
H2ONaCl

Відповіді:


372

Ось приклад, тому що приклад часто зрозуміліший, ніж довге пояснення. Припустимо foo, це змінна тип long. Наступна операція не є атомною операцією:

foo = 65465498L;

Дійсно, змінна записується за допомогою двох окремих операцій: перша, яка записує перші 32 біти, і друга, яка записує останні 32 біти. Це означає, що інший потік може прочитати значення fooта побачити проміжний стан.

Зробити операцію атомною полягає у використанні механізмів синхронізації для того, щоб переконатися, що операція бачиться з будь-якого іншого потоку, як єдиний, атомний (тобто не розділений на частини). Це означає, що будь-який інший потік, коли операція буде зроблена атомною, буде бачити значення fooдо призначення, або після призначення. Але ніколи проміжне значення.

Простий спосіб зробити це - зробити змінну мінливою :

private volatile long foo;

Або синхронізувати кожен доступ до змінної:

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

Або замінити його на AtomicLong:

private AtomicLong foo;

75
Отже, це припускають, що він працює в 32-бітній системі. Що робити, якщо це була 64-бітна система? Буде foo = 65465498L; бути атомним тоді?
Харке

46
@Harke Якщо ви використовуєте 64-бітну Java, так.
Jeroen

4
Чи це стосується і C # і .NET? Якщо так, то для того, щоб foo отримував атомарний бівівір, CLR повинен бути 64-розрядним?
Фабіано

5
@Fabiano Це застосовується, і ось, як цього досягти в .NET, оскільки у нас немає ключового слова синхронізованого типу Java. stackoverflow.com/questions/541194/…
The Muffin Man

2
Тоді припустимо, що потоку A присвоюється довге значення, а потім на половині потоку B намагається прочитати його. Якщо операція A є атомною, то потік B буде чекати, поки вона закінчиться? Це означає, що атомні операції забезпечать неявну безпеку потоку?
Теоман шипахі

60

"Атомна операція" означає операцію, яка видається миттєвою з точки зору всіх інших потоків. Вам не потрібно турбуватися про частково завершену операцію, коли діє гарантія.


25

Це щось, що "видається, що решта системи відбувається миттєво", і підпадає під категоризацію Лінеаризуемості в обчислювальних процесах. Щоб цитувати цю пов'язану статтю далі:

Атомність - це запорука ізоляції від одночасних процесів. Крім того, атомні операції зазвичай мають визначення успіху чи відмови - вони або успішно змінюють стан системи, або не мають явного ефекту.

Так, наприклад, у контексті системи баз даних можуть бути "атомні коміти", це означає, що ви можете натиснути набір змін оновлень на реляційну базу даних, і ці зміни будуть або всі подані, або жодна з них взагалі в у випадку відмови, таким чином дані не стають пошкодженими, і, як наслідок, блокування та / або черги, наступна операція буде іншим записом або зчитуванням, але тільки після факту. У контексті змінних і потоків це майже те саме, що застосовується до пам'яті.

Ваша цитата підкреслює, що такої поведінки не слід очікувати в усіх випадках.


15

Щойно знайшов пост Атомні проти Неатомні операції дуже корисним для мене.

"Операція, що діє на спільній пам'яті, є атомною, якщо вона виконується в один крок відносно інших потоків.

Коли запас атомів виконується на спільній пам'яті, жоден інший потік не може спостерігати модифікацію наполовину.

Коли атомне навантаження виконується на загальній змінній, воно зчитує все значення так, як воно з'явилося в один момент часу ".


14

Якщо у вас є декілька потоків, що виконують методи m1 та m2 у наведеному нижче коді:

class SomeClass {
    private int i = 0;

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

Ви маєте гарантію, що будь-який виклик потоку m2прочитає 0 або 5.

З іншого боку, з цим кодом (де iдовгий):

class SomeClass {
    private long i = 0;

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

потік виклику m2може читати 0, 1234567890L або якесь інше випадкове значення, тому що оператор i = 1234567890Lне гарантовано є атомарним для long(JVM може записати перші 32 біти та останні 32 біти за дві операції, і потік може спостерігати iміж ними) .


чому ви вважаєте, що "довгий" викликає проблему, а "int" - ні? Будь ласка, дивіться тут geekswithblogs.net/BlackRabbitCoder/archive/2012/08/09/…
onmyway133

1
@entropy довгі та подвійні завдання не гарантують атомний характер на Java. Таким чином, ви могли довго читати, коли після призначення було оновлено лише половину бітів.
assylias

0

У Java для читання і запису поля всіх типів, за винятком довгих і подвійних, відбувається атомно, і якщо поле оголошено за допомогою летючого модифікатора, навіть довге і подвійне атомно читаються і записуються. Тобто ми отримуємо на 100% або те, що там було, або те, що там сталося, і не може бути проміжного результату в змінних.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.