Атомные операции и многопоточность

17

Недавно я читал учебник, в котором я встретил заявление, в котором говорится.

«Спецификация языка Java гарантирует, что чтение или запись переменной является атомной операцией (если только переменная не имеет тип long или double ). Операционные переменные типа long или double являются только атомарными, если они объявлены с ключевым словом volatile . "

AtomicInteger или AtomicLong , который предоставляет такие методы, как getAndDecrement() , getAndIncrement() и getAndSet() , которые являются атомарными.

Я немного запутался в вышеупомянутом заявлении .. и не могли бы вы прояснить , когда использовать AtomicInteger или AtomicLong классов.

    
задан MaheshVarma 24.05.2013 в 09:03
источник

6 ответов

34

Выполнение a = 28a , являющееся en int ) является атомной операцией. Но выполнение a++ не является атомарной операцией, потому что для этого требуется чтение значения a, приращения и записи в результат. В результате, если вы использовали a++ для реализации потокобезопасного счетчика, вы могли бы одновременно использовать два потока, считывающих значение (например, 26), а затем увеличивать его и записывать одновременно, в результате чего получается 27, вместо 28.

AtomicInteger решает эту проблему, предоставляя атомные операции, такие как те, которые вы указали. В моем примере вы, например, использовали бы incrementAndGet() , что гарантировало бы, что конечное значение равно 28, а не 27.

    
ответ дан JB Nizet 24.05.2013 в 09:09
источник
6

Atomic означает, что операция завершается без какой-либо возможности для чего-то между ними. например. getAndDecrement (), на AtomicInteger, гарантирует, что переменная будет возвращена и уменьшена одновременно.

Если бы это была не атомная операция, существовала бы вероятность того, что значение будет уменьшаться (например, от 3 до 2), а затем изменено другим потоком (например, изменив его с 2 на 5), затем вернется как 5 .     

ответ дан Ren 24.05.2013 в 09:09
источник
2

Вам нужно AtomicInteger , если вам нужно прочитать переменную и записать результат в зависимости от значения чтения . Например, i++ читает i (например, 3 ) и записывает i+1 (например, 4 ). Тем временем поток может быть прерван, а три других потока увеличивают i . Теперь, когда мы вернемся, i фактически имеет значение 6 , но наш поток все еще записывает 4 , основываясь на том, что он читал заранее.

AtomicInteger.getAndIncrement гарантирует, что вы не прерваны , и поэтому всегда увеличивайте его. Более того, результат всегда закрашивается в память , тогда как нестабильный i не может быть сброшен в память. В этом случае другие потоки могут даже не видеть изменения.

    
ответ дан Matthias Meid 24.05.2013 в 09:14
источник
0

Я думаю, что это означает, что операция с длинным и двойным чтением является атомарной, а операция записи является атомарной. Но чтение + запись не является атомарным.

volatile long num;
num = num+1

Вышеуказанное не является потокобезопасным. Там чтение и запись - две отдельные операции. Каждый из них гарантированно является атомарным, но все выражение не является.

Чтобы сделать его потокобезопасным, вам нужно использовать AtomicLong и использовать функцию getAndIncrement.

    
ответ дан gkamal 24.05.2013 в 09:11
источник
0

Вы используете int или long, основываясь на верхнем / нижнем пределе диапазона номеров, с которым имеете дело. Пожалуйста, не смешивайте неатомное поведение с AtomicLong. Все, что вы написали выше, правильно, но вы, вероятно, смешиваете обе концепции. AtomicXXX более полезны в тех случаях, когда вы выполняете операции «сравнивать и настраивать». Например, даже если int может быть изменен / прочитан, атомарно следующий код будет неправильным в многопоточной среде:

int i =10
..
..
..
if(i == 10) i++;

в многопоточной среде два потока могут получить доступ к этому коду с атомарным и обновленным значением i и сделать его в согласованном состоянии. SO справляется с такими ситуациями, как правило, вы защищаете код «if (i == 10) i ++;» с синхронизированным блоком. Однако класс AtomicInteger предоставляет API для достижения таких целей без использования синхронизированных блоков, которые работают медленнее. То же самое относится к API-интерфейсам AtmoicLong

    
ответ дан Saurabh 24.05.2013 в 09:12
источник
0

атомарность операции требуется, когда вы мутируете переменную. Выполнение int a = 10; - это атомная операция, но не та, которая даст вам проблему. операции с заданием обычно являются мутативными типами a++ или a = a + 2; и т. д.

Спецификация Java гарантирует, что «чтение» и «запись» являются атомарными операциями, а не их комбинациями. поэтому операция, которая «читает, добавляет 1, а затем записывает результат назад», не является атомарной согласно спецификации. такие операции называются составными операциями, и они обычно должны быть атомарными в контексте их использования в нашем коде.

Атомные типы помогают решить эту проблему. используя incrementAndget () для атомарного типа, делает «чтение», добавляет 1, а затем записывает результат назад и считывает новый результат «одна атомная операция в контексте безопасности потоков».

Надеюсь, это поможет. Кстати, вы должны это прочитать ( Ссылка ). об основах параллелизма и потоков. это прекрасно объясняет этот материал.

    
ответ дан Nazgul 02.12.2013 в 12:09
источник