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

20

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

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

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

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

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

6 ответов

41

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

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

    
ответ дан JB Nizet 24.05.2013 в 09:09
  • Тогда почему мы должны использовать «volatile», если мы выполняем операции с переменными типа «double» или «long»? –  MaheshVarma 24.05.2013 в 09:19
  • Если вы используете AtomicXxx, вам не нужно использовать volatile, поскольку атомные объекты предоставляют больше гарантий, чем летучие. Выполнение a ++ в нестабильном целое не делает операцию атомой. Это гарантирует, что другой поток увидит новое значение a после его увеличения. При назначении новых значений длинным или двойным значениям также гарантируется, что запись является атомарной операцией (поскольку запись в энергонезависимую длинную или двойную переменную заключается в записи 4 байтов, а затем запись других 4 байтов). Предпочитают атомарные объекты над изменчивыми переменными в целом. –  JB Nizet 24.05.2013 в 09:26
  • a = 28 не гарантируется быть атомарным, если a является длинным или двойным, если он не является также изменчивым. –  assylias 25.05.2013 в 10:39
  • @assylias: да. Я не повторил этого, потому что это было правильно указано в вопросе. Однако я отредактирую, чтобы уточнить. –  JB Nizet 26.05.2013 в 09:04
  • long и double не являются атомарными в 32-битных JVM, потому что они составляют 8 байтов. Они являются атомарными в 64-битных JVM. –  fracca 31.10.2013 в 19:37
Показать остальные комментарии
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
  • Он говорит прямо противоположное: в длинном a; a = 1 ;, второе утверждение не гарантируется атомарным. –  assylias 25.05.2013 в 10:36
  • no it does not - «Операционные переменные типа long или double являются только атомарными, если они объявлены с помощью volatile ключевого слова», обратите внимание на изменчивый –  gkamal 25.05.2013 в 13:17
  • Ваше первое предложение вводит в заблуждение без ссылки на volatile. –  assylias 25.05.2013 в 14:14
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