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?
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:
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.
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++;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With