What Operations in Java Are Considered Atomic

What operations in Java are considered atomic?

  • all assignments of primitive types except for long and double
  • all assignments of references
  • all assignments of volatile variables
  • all operations of java.concurrent.Atomic* classes

and maybe something more. Look at the jls.

As noted in the comments, atomicity does not imply visibility. So while another thread is guaranteed not to see a partially written int, it may never see the new value.

The operations on long and double are on common 64 bit CPUs atomic as well, although there's no guarantee. See also this feature request.

Volatile and atomic operation in java

An operation is atomic if no thread will see an intermediary state, i.e. the operation will either have completed fully, or not at all.

Reading an int field is an atomic operation, i.e. all 32 bits are read at once. Writing an int field is also atomic, the field will either have been written fully, or not at all.

However, the method doSomething() is not atomic; a thread may yield the CPU to another thread while the method is being executing, and that thread may see that some, but not all, operations have been executed.

That is, if threads T1 and T2 both execute doSomething(), the following may happen:

T1: num = 10;
T2: num = 10;
T1: System.out.println(num); // prints 10
T1: num = 20;
T1: System.out.println(num); // prints 20
T2: System.out.println(num); // prints 20
T2: num = 20;
T2: System.out.println(num); // prints 20

If doSomething() were synchronized, its atomicity would be guaranteed, and the above scenario impossible.

What does atomic mean in programming?

Here's an example: Suppose foo is a variable of type long, then the following operation is not an atomic operation (in Java):

foo = 65465498L;

Indeed, the variable is written using two separate operations: one that writes the first 32 bits, and a second one which writes the last 32 bits. That means that another thread might read the value of foo, and see the intermediate state.

Making the operation atomic consists in using synchronization mechanisms in order to make sure that the operation is seen, from any other thread, as a single, atomic (i.e. not splittable in parts), operation. That means that any other thread, once the operation is made atomic, will either see the value of foo before the assignment, or after the assignment. But never the intermediate value.

A simple way of doing this is to make the variable volatile:

private volatile long foo;

Or to synchronize every access to the variable:

public synchronized void setFoo(long value) {
this.foo = value;
}

public synchronized long getFoo() {
return this.foo;
}
// no other use of foo outside of these two methods, unless also synchronized

Or to replace it with an AtomicLong:

private AtomicLong foo;

Volatile Vs Atomic

The effect of the volatile keyword is approximately that each individual read or write operation on that variable is made atomically visible to all threads.

Notably, however, an operation that requires more than one read/write -- such as i++, which is equivalent to i = i + 1, which does one read and one write -- is not atomic, since another thread may write to i between the read and the write.

The Atomic classes, like AtomicInteger and AtomicReference, provide a wider variety of operations atomically, specifically including increment for AtomicInteger.

Atomic Operations and multithreading

Doing a = 28 (with a being an int) is an atomic operation. But doing a++ is not an atomic operation because it requires a read of the value of a, an incrementation, and a write to a of the result. As a result, if you used a++ to implement a thread-safe counter, you could have two threads reading the value concurrently (26 for example), then have both increment it and writing it concurrently, resulting in 27 as a result, instead of 28.

AtomicInteger solves this issue by providing atomic operations like the ones you listed. In my example, you would use incrementAndGet() for example, which would guarantee the end value is 28 and not 27.

are all instructions in jvm atomic?

The bytecode instructions you mentioned (iinc, iload, aload etc.) operate on the values from the operand stack and on the local variables. These areas are thread-local (see JVMS 2.5, 2.6), that is, speaking about atomicity here is meaningless. I.e. they are NOT implemented using atomic CPU instructions like lock xadd, but nobody should care.

The bytecode instructions that read or write fields and array elements (getfield, putfiled, iastore etc.) are atomic except for long and double types (see JLS 17.7). 32-bit JVM may implement (actually, HotSpot JVM does implement) reading and writing of 64-bit fields with two 32-bit loads or stores, unless the field is declared volatile.

Why is this statement considered Atomic?

In your first case you will always encounter y either before or after the increment. Since operations are async you can't tell which, but it doesn't make any difference -- the only observable effect is that x is larger by 1 in one case rather than the other, and this could have occurred based on ordering of the two statements.

In the second case, you can get a situation where the resulting values of x and y are not consistent with either ordering of the statements, because x and y were both fetched before either was modified.

The first case is not really Java's definition of "atomic" (and has nothing to do with any "atomic" directives that might be generated by the compiler), but simple programming.

What is atomic?

If the parameter in the above method was a reference to an object,
then the operation would not be atomic, right?

In general this is correct. A good rule of thumb is to consider there is no atomicity at all, even with primitives like:

int b,c; 
int a = ++b - c;

only primitives, but the whole assignment is potential not atomic.

If you need enshure atomic operations you have diferent posibilities:

  • synchronised blocks
  • immutable objects
  • specific libraries (java.util.concurrent.atomic)


Related Topics



Leave a reply



Submit