10

I've read in Oracle docs that:

  • Reads and writes are atomic for reference variables and for most
    primitive variables (all types except long and double).

(I guess this feature has been added in some new JDK release because I used to think that reads/writes of ALL primitive variables are NOT atomic)

Does it mean that AtomicInteger is deprecated and shouldn't be used in new projects?

Community
  • 1
  • 1
frostman
  • 566
  • 2
  • 12
  • 25
  • 4
    The atomicity of writes has little to nothing to do with the usecases of `AtomicInteger`. E.g. `compareAndSet` is still very much important and has nothing to do with the atomicity of its writes. – luk2302 Feb 05 '17 at 16:40
  • 2
    All primitives except `long` and `double` have always been atomic for reads and writes. This hasn't changed for `int`. – Peter Lawrey Feb 05 '17 at 17:01
  • 1
    What if atomicity isn't all you need? What if you need, for example, visibility guarantees too? – David Schwartz Feb 05 '17 at 17:26

4 Answers4

21

While a single store to or a single load from an ordinary int is atomic in Java, you cannot atomically, say, increment it. Doing so would require you to first load the value, then compute the new value depending on it and then store the new value back. But between the two accesses, another thread might have modified the value. AtomicInteger provides operations like getAndIncrement that can be used for this purpose without having to use a lock.

5gon12eder
  • 24,280
  • 5
  • 45
  • 92
7

Deprecated? Not at all. While individual reads and writes of primitive variables are atomic, AtomicInteger (and the other atomic classes in java.util.concurrent.atomic) provides more complex operations that are also atomic. These include things like addAndGet(int), which are not at all atomic for primitive int variables. Thus,

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

(Both of the comments above are under the assumption that i and j are not changed by the time the statement in question starts executing, but might be changed by another thread after execution starts but before it is finished.)

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
5

Does it mean that AtomicInteger is deprecated and shouldn't be used in new projects?

No. First and most obvious, if it were deprecated, it would be marked as such.

Furthermore, AtomicInteger and primitive int simply aren't interchangeable. There are a lot of differences, but here are the first three that spring to mind:

  • AtomicInteger can be passed by reference, unlike primitive int, which is passed by value.
  • AtomicInteger has a some operations, such as compareAndSet(), that are unavailable on primitives.
  • Even those that superficially look the same, namely AtomicInteger.getAndIncrement() vs. int++ are different; the former is atomic, the second is two operations that are not atomic together.

I guess this feature has been added in some new JDK release because I used to think that reads/writes of ALL primitive variables are NOT atomic

Reads and writes of primitives of 32-bits or smaller have always been atomic.

Barend
  • 17,296
  • 2
  • 61
  • 80
  • 2
    Nothing in Java is "passed by reference." The idea that you are trying to describe is that `AtomicInteger` is a _reference type_. Every parameter of type `AtomicInteger` is a reference to some object, as is every local variable and every field of that type. If I have `AtomicInteger ai=someAtomicInteger;` and I called a pass-by-reference function, `foobar(ai)`, then the `foobar` function would be able to change `ai` to refer to some _other_ AtomicInteger object. That can never happen in Java. Java is always call by value. It's just that, in Java, "values" often are references. – Solomon Slow Feb 05 '17 at 20:13
  • Of course it's passed by reference, as the RValue is never duplicated(that's what pass by value always means). Unfortunately, for historical reasons, some people cling to the idea that pass by reference means pointing to the original variable and the ability to swap values - which is wrong, no one cares about that, almost all uses of pass-by-reference are made in order to avoid the duplication of the RValue - that is the actual data that's being processed, which can be very large. – ccdan Mar 16 '19 at 07:09
3

The other answers addressed why AtomicInteger is needed. I'd like to clarify what that document is talking about.

The use of the term atomic in that document isn't for the same purpose as its use in AtomicInteger.

That document also states

Atomic actions cannot be interleaved, so they can be used without fear of thread interference.

This refers to

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

thread 3 is guaranteed to see either the value 1 or the value 2.

However, with long or double, you don't have that guarantee. The Java Language Specification states

For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64-bit value from one write, and the second 32 bits from another write.

So, for example,

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

thread 3 is allowed to see the first 32 bits from thread 1's assignment and the last 32 bits from thread 2's assignment, creating the long value 7fff_ffff_ffff_ffff. The same can occur for double.

Modifying your long or double variable with volatile prevents this behavior.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724