Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can synchronized blocks be faster than Atomics?

Suppose two following counter implementations:

class Counter {
  private final AtomicInteger atomic = new AtomicInteger(0);
  private int i = 0;

  public void incrementAtomic() {
    atomic.incrementAndGet();
  }

  public synchronized void increment() {
    i++;
  }
}

At first glance atomics should be faster and more scalable. And they are, I believe. But are they faster than synchronized blocks all the time? Or some situations exists when this rule is broken (e.g. SMP/Single CPU machine, different CPU ISA, OS'es etc.)?

like image 941
Denis Bazhenov Avatar asked Jul 18 '12 23:07

Denis Bazhenov


People also ask

Why is AtomicInteger class better than a synchronized counter class?

The AtomicInteger class uses CAS (compare-and-swap) low-level CPU operations (no synchronization needed!) They allow you to modify a particular variable only if the present value is equal to something else (and is returned successfully).

Why Atomic has volatile and synchronization?

Atomic variables help with handling non-atomic operations like increment-decrement, or any operations, which need to read the value before assigning a new one. Atomic values are a simple and convenient way to resolve synchronization issues in our code.

Does volatile make non atomic operation to Atomic?

The volatile keyword is used: to make non atomic 64-bit operations atomic: long and double . (all other, primitive accesses are already guaranteed to be atomic!)

Is volatile variable is atomic?

We can use the volatile keyword with variables. Using a volatile keyword with classes and methods is illegal. When we have our variables as volatile, they are always visible to other threads. If you declared the variable as volatile, Read and Writes are atomic.


1 Answers

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.

like image 93
Tom Hawtin - tackline Avatar answered Sep 20 '22 00:09

Tom Hawtin - tackline