Suppose two following counter implementations:
class Counter {
private final AtomicInteger atomic = new AtomicInteger(0);
private int i = 0;
public void i
atomic variables will always be faster.
you can see that the java.util.concurrent package always utilises atomic variables rather than synchronized blocks.
It's implementation dependent - so ultimately you'll need to benchmark on your particualar platform / JVM / configuration.
Having said that, atomics should always be faster for the following reasons:
synchronized
makes use of relatively heavyweight locking schemes with monitor objects, which is designed to protect potentially large blocks of code. This form of locking in inherently more complicated than atomic operations so you would expect it to have higher runtime cost.Or some situations exists when this rule is broken (e.g. SMP/Single CPU machine, different CPU ISA, OS'es etc.)?
I don't know of any. (And I stand ready to be corrected ... if someone knows of a concrete counter-example.)
However (and this is my main point) there is no theoretical reason why you couldn't have a hardware architecture or a poorly implemented JVM in which synchronized
is the same speed or faster than the atomic
types. (The relative speed of these two forms of synchronization is an implementation issue, and as such can only be quantified for existing implementations.)
And of course, this doesn't mean you should never use synchronized
. The synchronized
construct has many use-cases that the atomic classes don't address.
As others have said this is implementation dependent . But keep in mind that if your program invariants involve more than one variable , then you have to use synchronization to update them together . You can't do atomic operation on two related variables together just because they are of Atomic type. In that case your only friend is synchronized .
incrementAndGet
may well be implemented as a CAS-loop. In highly contented situations that can result in n-1 of your threads failing leading to an O(n) problem, for n threads.
( For @Geek:
Typically getAndIncrement
may be implemented something like:
int old;
do {
old = value;
} while (!compareAndSet(value, old, old+1));
return old;
Imagine you have a n threads executing this code on the same atomic, and they happen to be in step with each other. The first iteration does kn work. Only one CAS will succeed. The other n-1 threads will repeat the exercise until there is only one left. So the total work is O(n^2) (worst case) instead of O(n). )
Having said that, acquiring a lock will need to do something similar at best, and locks aren't at their best when heavily contended. You're not likely to see much of an advantage for locks until you use a CAS-loop which requires significant computation before get and compareAndSwap.