What does "atomic" mean in programming?
JavaAtomicJava Problem Overview
In the Effective Java book, it states:
> The language specification guarantees that reading or writing a
> variable is atomic unless the variable is of type long
or double
[JLS,
> 17.4.7].
What does "atomic" mean in the context of Java programming, or programming in general?
Java Solutions
Solution 1 - Java
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;
Solution 2 - Java
"Atomic operation" means an operation that appears to be instantaneous from the perspective of all other threads. You don't need to worry about a partly complete operation when the guarantee applies.
Solution 3 - Java
It's something that "appears to the rest of the system to occur instantaneously", and falls under categorisation of Linearizability in computing processes. To quote that linked article further:
> Atomicity is a guarantee of isolation from concurrent processes. > Additionally, atomic operations commonly have a succeed-or-fail > definition — they either successfully change the state of the system, > or have no apparent effect.
So, for instance, in the context of a database system, one can have 'atomic commits', meaning that you can push a changeset of updates to a relational database and those changes will either all be submitted, or none of them at all in the event of failure, in this way data does not become corrupt, and consequential of locks and/or queues, the next operation will be a different write or a read, but only after the fact. In the context of variables and threading this is much the same, applied to memory.
Your quote highlights that this need not be expected behaviour in all instances.
Solution 4 - Java
Just found a post [Atomic vs. Non-Atomic Operations][1] to be very helpful to me.
> "An operation acting on shared memory is atomic if it completes in a single step relative to other threads.
> When an atomic store is performed on a shared memory, no other thread can observe the modification half-complete.
> When an atomic load is performed on a shared variable, it reads the entire value as it appeared at a single moment in time."
[1]: http://preshing.com/20130618/atomic-vs-non-atomic-operations/ "Atomic vs. Non-Atomic Operations"
Solution 5 - Java
If you have several threads executing the methods m1 and m2 in the code below:
class SomeClass {
private int i = 0;
public void m1() { i = 5; }
public int m2() { return i; }
}
you have the guarantee that any thread calling m2
will either read 0 or 5.
On the other hand, with this code (where i
is a long):
class SomeClass {
private long i = 0;
public void m1() { i = 1234567890L; }
public long m2() { return i; }
}
a thread calling m2
could read 0, 1234567890L, or some other random value because the statement i = 1234567890L
is not guaranteed to be atomic for a long
(a JVM could write the first 32 bits and the last 32 bits in two operations and a thread might observe i
in between).
Solution 6 - Java
In Java reading and writing fields of all types except long and double occurs atomically, and if the field is declared with the volatile modifier, even long and double are atomically read and written. That is, we get 100% either what was there, or what happened there, nor can there be any intermediate result in the variables.