Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which is the difference between AtomicReference and Synchronized?

Is there any difference between AtomicReference and Synchronized?
E.G.

public class Internet {
    AtomicReference<String> address;
    public String getAddress(){
        return address.toString();
    }
    public void setAddress(String address) {
        this.address.set(address);
    }

}

And I pass the class to some threads that try to use the class at the same time, is it the same thing if I use this:

public class Internet {
    String address;
    public String getAddress(){
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}

And then in the thread use synchronized before access to the class?

like image 467
Tinwor Avatar asked Mar 03 '15 14:03

Tinwor


3 Answers

You didn't initialize the reference in the first example, it should probably be:

public class Internet {
    AtomicReference<String> address = new AtomicReference<String>();
    public String getAddress(){
        String s = address.get();
        return s == null ? null : s.toString();
    }
    public void setAddress(String address) {
        this.address.set(address);
    }
}

Where the access restriction is located is important. If you put the control within the object being accessed then it can have sole control of its invariants, which is much less fragile than relying on the threads to all synchronize properly, where one badly behaved accessing thread can corrupt the thing being accessed. So the first example is much better on that account.

If you change the second example so that the object has control over its own locking (so it is not relying on threads accessing it to do so safely), like this:

public class Internet {
    private final Object lock = new Object();
    private String s;
    public String getAddress() {
       synchronized(lock) {
           return s;
       }
    }
    public void setAddress(String s) {
        synchronized(lock) {
            this.s = s;
        }
    }
}

then it's a closer comparison, one relies on locking and the other on atomic references. The one using AtomicReference tries to avoid locking using machine-level atomic processing instructions. Which is faster may depend on your hardware and jvm and the processing load, typically the atomic approach should be faster. The synchronized approach is a more general purpose mechanism; with the synchronized block you can group together multiple assignments much more easily, where with atomic references it's much more involved.

As James says in his answer, with synchronization your threads are waiting for a lock; there is no timeout, and deadlock is possible. With the atomic reference the thread makes its change with no waiting on a shared lock.

The simplest and best-performing way to implement this would be to organize your code so that you can make the object immutable, so you would avoid all locking, busy-waiting, and cache updating:

public final class Internet {
    private final String s;
    public Internet(String s) {
        this.s = s;
    }
    public String getAddress() {return s;}
}

In descending order of preference:

  • Prefer immutability whenever possible.
  • For code that can't be immutable, try to confine mutation to a thread.
  • If only one thing has to change across threads, use the atomic approach.
  • If multiple changes across threads need to occur together undisturbed by other threads, use locking.
like image 93
Nathan Hughes Avatar answered Oct 29 '22 22:10

Nathan Hughes


There's nothing wrong with the other answers here if you can understand them, but they mostly seem to focus on details, nomenclature, and use-cases, while skipping over the big picture that "everybody" already knows.

Here's the big picture---the difference between an AtomicFoobar operation and a synchronized block.

An AtomicFoobar operation (e.g., atomicReference.compareAndSet(...)) either performs exactly one, very simple, thread-safe operation, or else it fails. Regardless of whether it succeeeds or fails, it will never make the thread wait.

A synchronized block, on the other hand is as complicated as you make it---there is no limit to how many statements are executed while the lock is locked. A synchronized block will never fail, but it may make the calling thread wait until the operation(s) can be safely performed.

On most architectures, each AtomicFoobar methods is implemented as a Java native method (i.e., C code) that executes a single, specialized hardware instruction. Synchronized, on the other hand is most always implemented with operating system calls which, somewhere deep in the guts, probably make use of the same hardware instructions.

like image 6
Solomon Slow Avatar answered Oct 29 '22 22:10

Solomon Slow


A synchronized method/block blocks all access to that method/block from other threads while one thread is performing the method.

An Atomic... can be accessed by many threads at once - there are usually CAS access methods available for them to help with high-speed access.

As such - they are completely different but they can sometimes be used to solve parallel accessibility issues.

These two classes use the two different methods to return a steadily increasing number such that the same number will never be delivered twice. The AtomicInteger version will run faster in a high-load environment. The one using synchronized will work in Java 4 and older.

public class Incremental1 {

    private AtomicInteger n = new AtomicInteger();

    public Integer nextNumber() {
        // Use the Atomic CAS function to increment the number.
        return n.getAndIncrement();
    }
}

public class Incremental2 {

    private int n = 0;

    public synchronized Integer nextNumber() {
        // No two threads can get in here at the same time.
        return n++;
    }

}
like image 2
OldCurmudgeon Avatar answered Oct 30 '22 00:10

OldCurmudgeon