2013-05-24 2 views
18

Недавно я читал учебник, в котором я наткнулся на утверждение, что говорит ..атомарных операций и многопоточности

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

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

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

ответ

35

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

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

+1

Тогда почему мы должны использовать «volatile», если мы выполняем операции с переменными типа «double» или «long»? – MaheshVarma

+6

Если вы используете AtomicXxx, вам не нужно использовать volatile, поскольку атомные объекты предоставляют больше гарантий, чем летучие. Выполнение 'a ++' в энергонезависимом целой не делает операцию атомой. Это гарантирует, что другой поток увидит новое значение a после его увеличения. При назначении новых значений длинным или двойным значениям также гарантируется, что запись является атомарной операцией (поскольку запись в энергонезависимую длинную или двойную переменную заключается в записи 4 байтов, а затем запись других 4 байтов). Предпочитают атомарные объекты над изменчивыми переменными в целом. –

+1

'a = 28' не гарантированно является атомарным, если' a' является длинным или двойным, если оно не является также изменчивым. – assylias

6

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

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

0

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

volatile long num; 
num = num+1 

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

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

+0

В нем говорится полная противоположность: в 'long a; a = 1; ', второе утверждение не гарантируется атомарным. – assylias

+0

no it does not - «Операционные переменные типа long или double являются только атомарными, если они объявлены с помощью volatile ключевого слова», обратите внимание на volatile – gkamal

+0

Ваше первое предложение вводит в заблуждение без ссылки на volatile. – assylias

0

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

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

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

2

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

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

0

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

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

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

Надеюсь, это поможет. Кстати, вы должны прочитать статью (http://walivi.wordpress.com/2013/08/24/concurrency-in-java-a-beginners-introduction/) об основах параллелизма и потоков. это прекрасно объясняет этот материал.

Смежные вопросы