2017-02-05 11 views
7

Я прочитал в Oracle docs, что:Почему AtomicInteger необходим, если записи и чтения для переменных int являются атомарными?

  • чтение и записи атомного для ссылочных переменных и для большинства
    примитивных переменных (всех типов кроме долго и дважды).

(я предполагаю, что эта функция была добавлена ​​в какой-то новой версии JDK, потому что я привык думать, что чтение/запись всех примитивных переменных не являются атомарными)

Означает ли это, что AtomicInteger устарела и не должны использоваться в новых проектах?

+4

Атомарность записей практически не имеет отношения к использованию «AtomicInteger». Например. 'compareAndSet' по-прежнему очень важен и не имеет ничего общего с атомарностью его записей. – luk2302

+2

Все примитивы, кроме 'long' и' double', всегда были атомарными для чтения и записи. Это не изменилось для 'int'. –

+1

Что делать, если атомичность - это еще не все, что вам нужно? Что делать, если вам нужны, например, гарантии на видимость? –

ответ

14

Хотя один магазин до или одной нагрузка от обычного int является атомарной Java, вы не можете атомарны, скажем, увеличить его.Для этого потребуется сначала загрузить значение, а затем вычислить новое значение в зависимости от него, а затем сохранить новое значение обратно. Но между двумя обращениями другой поток может изменить значение. AtomicInteger предоставляет такие операции, как getAndIncrement, которые могут использоваться для этой цели без использования блокировки.

7

Устаревшие? Не за что. Хотя отдельные чтения и записи примитивных переменных являются атомарными, AtomicInteger (и другие атомные классы в java.util.concurrent.atomic) обеспечивают более сложные операции, которые также являются атомарными. К ним относятся такие вещи, как addAndGet(int), которые вовсе не являются атомарными для примитивных переменных int. Таким образом,

int i = 3; 
AtomicInteger j = new AtomicInteger(3); 
i += 5; // NOT thread-safe -- might not set i to 8 
int n = j.addAndGet(5); // thread-safe -- always sets n to 8 

(Оба замечания выше в предположении, что i и j не изменяются по времени заявление в вопросе начинается выполнения, но может быть изменен другим потоком после начала выполнения, но до оно завершено.)

5

Означает ли это, что AtomicInteger устарел и не должен использоваться в новых проектах?

№. Первое и наиболее очевидное, если оно было устаревшим, оно было бы отмечено как таковое.

Кроме того, AtomicInteger и примитивные int просто не взаимозаменяемы. Есть много различий, но вот первые три, которые приходят на ум:

  • AtomicInteger может передаваться по ссылке, в отличие от примитивного int, который передается по значению.
  • У AtomicInteger есть некоторые операции, такие как compareAndSet(), которые недоступны для примитивов.
  • Даже те, что внешне выглядят одинаково, а именно AtomicInteger.getAndIncrement() против int++ различны; первая - атомарная, вторая - две операции, которые не являются атомами вместе.

Я предполагаю, что эта функция была добавлена ​​в какой-то новой версии JDK, потому что я привык думать, что чтение/запись всех примитивных переменных не являются атомарными

Чтение и запись примитивов 32- бит или меньше всегда были атомарными.

+2

Ничто в Java не передается по ссылке. Идея, которую вы пытаетесь описать, заключается в том, что 'AtomicInteger' является _reference type_. Каждый параметр типа «AtomicInteger» является ссылкой на некоторый объект, как и любая локальная переменная и каждое поле этого типа. Если у меня есть 'AtomicInteger ai = someAtomicInteger', и я вызывал функцию pass-by-reference,' foobar (ai) ', то функция foobar могла бы изменить' ai', чтобы ссылаться на какой-то объект _other_ AtomicInteger. Этого никогда не может быть в Java. Java всегда вызывается по значению.Это просто, что в Java «значения» часто являются ссылками. –

3

Другие ответы на вопросы, почему требуется AtomicInteger. Я хотел бы уточнить, о чем говорит этот документ.

Использование термина атома в этом документе не с той же целью, как и его использование в AtomicInteger.

Этот документ также утверждает

Атомные действия не могут чередоваться, поэтому они могут быть использованы без страха резьбы вмешательства.

Это относится к

int x; 
x = 1; // thread 1 
x = 2; // thread 2 
System.out.println(x); // thread 3 

thread 3 гарантированно увидеть либо значение 1 или значение 2.

Однако, с long или double, у вас нет этой гарантии. Java Language Specification заявляет

Для целей модели памяти языка программирования Java, а одного записи в энергонезависимую long или double значения рассматриваются как два отдельных пишут: один к каждому 32-битной половине. Это может привести к ситуации , когда поток видит первые 32 бита 64-битного значения от , один напишет, а второй 32 бита из другой записи.

Так, например,

long x; 
x = 0xffff_ffffL; // thread 1 
x = 0x7fff_ffff_0000_0000L; // thread 2 
System.out.println(x); // thread 3 

thread 3 допускается, чтобы увидеть первый 32 бита из thread 1 'присвоения ей и последние 32 бит из thread 2' присвоения ей, создавая значение 7fff_ffff_ffff_fffflong. То же самое может произойти для double.

Изменение этой переменной long или double с volatile предотвращает это поведение.