What is the difference between atomic / volatile / synchronized?

后端 未结 7 1010
长情又很酷
长情又很酷 2020-11-22 16:56

How do atomic / volatile / synchronized work internally?

What is the difference between the following code blocks?

Code 1

private int counter         


        
相关标签:
7条回答
  • 2020-11-22 17:10

    Synchronized Vs Atomic Vs Volatile:

    • Volatile and Atomic is apply only on variable , While Synchronized apply on method.
    • Volatile ensure about visibility not atomicity/consistency of object , While other both ensure about visibility and atomicity.
    • Volatile variable store in RAM and it’s faster in access but we can’t achive Thread safety or synchronization whitout synchronized keyword.
    • Synchronized implemented as synchronized block or synchronized method while both not. We can thread safe multiple line of code with help of synchronized keyword while with both we can’t achieve the same.
    • Synchronized can lock the same class object or different class object while both can’t.

    Please correct me if anything i missed.

    0 讨论(0)
  • 2020-11-22 17:23

    I know that two threads can not enter in Synchronize block at the same time

    Two thread cannot enter a synchronized block on the same object twice. This means that two threads can enter the same block on different objects. This confusion can lead to code like this.

    private Integer i = 0;
    
    synchronized(i) {
       i++;
    }
    

    This will not behave as expected as it could be locking on a different object each time.

    if this is true than How this atomic.incrementAndGet() works without Synchronize ?? and is thread safe ??

    yes. It doesn't use locking to achieve thread safety.

    If you want to know how they work in more detail, you can read the code for them.

    And what is difference between internal reading and writing to Volatile Variable / Atomic Variable ??

    Atomic class uses volatile fields. There is no difference in the field. The difference is the operations performed. The Atomic classes use CompareAndSwap or CAS operations.

    i read in some article that thread has local copy of variables what is that ??

    I can only assume that it referring to the fact that each CPU has its own cached view of memory which can be different from every other CPU. To ensure that your CPU has a consistent view of data, you need to use thread safety techniques.

    This is only an issue when memory is shared at least one thread updates it.

    0 讨论(0)
  • 2020-11-22 17:24

    volatile:

    volatile is a keyword. volatile forces all threads to get latest value of the variable from main memory instead of cache. No locking is required to access volatile variables. All threads can access volatile variable value at same time.

    Using volatile variables reduces the risk of memory consistency errors, because any write to a volatile variable establishes a happens-before relationship with subsequent reads of that same variable.

    This means that changes to a volatile variable are always visible to other threads. What's more, it also means that when a thread reads a volatile variable, it sees not just the latest change to the volatile, but also the side effects of the code that led up the change.

    When to use: One thread modifies the data and other threads have to read latest value of data. Other threads will take some action but they won't update data.

    AtomicXXX:

    AtomicXXX classes support lock-free thread-safe programming on single variables. These AtomicXXX classes (like AtomicInteger) resolves memory inconsistency errors / side effects of modification of volatile variables, which have been accessed in multiple threads.

    When to use: Multiple threads can read and modify data.

    synchronized:

    synchronized is keyword used to guard a method or code block. By making method as synchronized has two effects:

    1. First, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.

    2. Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads.

    When to use: Multiple threads can read and modify data. Your business logic not only update the data but also executes atomic operations

    AtomicXXX is equivalent of volatile + synchronized even though the implementation is different. AmtomicXXX extends volatile variables + compareAndSet methods but does not use synchronization.

    Related SE questions:

    Difference between volatile and synchronized in Java

    Volatile boolean vs AtomicBoolean

    Good articles to read: ( Above content is taken from these documentation pages)

    https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html

    https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html

    https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html

    0 讨论(0)
  • 2020-11-22 17:26

    The Java volatile modifier is an example of a special mechanism to guarantee that communication happens between threads. When one thread writes to a volatile variable, and another thread sees that write, the first thread is telling the second about all of the contents of memory up until it performed the write to that volatile variable.

    Atomic operations are performed in a single unit of task without interference from other operations. Atomic operations are necessity in multi-threaded environment to avoid data inconsistency.

    0 讨论(0)
  • 2020-11-22 17:27

    A volatile + synchronization is a fool proof solution for an operation(statement) to be fully atomic which includes multiple instructions to the CPU.

    Say for eg:volatile int i = 2; i++, which is nothing but i = i + 1; which makes i as the value 3 in the memory after the execution of this statement. This includes reading the existing value from memory for i(which is 2), load into the CPU accumulator register and do with the calculation by increment the existing value with one(2 + 1 = 3 in accumulator) and then write back that incremented value back to the memory. These operations are not atomic enough though the value is of i is volatile. i being volatile guarantees only that a SINGLE read/write from memory is atomic and not with MULTIPLE. Hence, we need to have synchronized also around i++ to keep it to be fool proof atomic statement. Remember the fact that a statement includes multiple statements.

    Hope the explanation is clear enough.

    0 讨论(0)
  • 2020-11-22 17:30

    You are specifically asking about how they internally work, so here you are:

    No synchronization

    private int counter;
    
    public int getNextUniqueIndex() {
      return counter++; 
    }
    

    It basically reads value from memory, increments it and puts back to memory. This works in single thread but nowadays, in the era of multi-core, multi-CPU, multi-level caches it won't work correctly. First of all it introduces race condition (several threads can read the value at the same time), but also visibility problems. The value might only be stored in "local" CPU memory (some cache) and not be visible for other CPUs/cores (and thus - threads). This is why many refer to local copy of a variable in a thread. It is very unsafe. Consider this popular but broken thread-stopping code:

    private boolean stopped;
    
    public void run() {
        while(!stopped) {
            //do some work
        }
    }
    
    public void pleaseStop() {
        stopped = true;
    }
    

    Add volatile to stopped variable and it works fine - if any other thread modifies stopped variable via pleaseStop() method, you are guaranteed to see that change immediately in working thread's while(!stopped) loop. BTW this is not a good way to interrupt a thread either, see: How to stop a thread that is running forever without any use and Stopping a specific java thread.

    AtomicInteger

    private AtomicInteger counter = new AtomicInteger();
    
    public int getNextUniqueIndex() {
      return counter.getAndIncrement();
    }
    

    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). So when you execute getAndIncrement() it actually runs in a loop (simplified real implementation):

    int current;
    do {
      current = get();
    } while(!compareAndSet(current, current + 1));
    

    So basically: read; try to store incremented value; if not successful (the value is no longer equal to current), read and try again. The compareAndSet() is implemented in native code (assembly).

    volatile without synchronization

    private volatile int counter;
    
    public int getNextUniqueIndex() {
      return counter++; 
    }
    

    This code is not correct. It fixes the visibility issue (volatile makes sure other threads can see change made to counter) but still has a race condition. This has been explained multiple times: pre/post-incrementation is not atomic.

    The only side effect of volatile is "flushing" caches so that all other parties see the freshest version of the data. This is too strict in most situations; that is why volatile is not default.

    volatile without synchronization (2)

    volatile int i = 0;
    void incIBy5() {
      i += 5;
    }
    

    The same problem as above, but even worse because i is not private. The race condition is still present. Why is it a problem? If, say, two threads run this code simultaneously, the output might be + 5 or + 10. However, you are guaranteed to see the change.

    Multiple independent synchronized

    void incIBy5() {
      int temp;
      synchronized(i) { temp = i }
      synchronized(i) { i = temp + 5 }
    }
    

    Surprise, this code is incorrect as well. In fact, it is completely wrong. First of all you are synchronizing on i, which is about to be changed (moreover, i is a primitive, so I guess you are synchronizing on a temporary Integer created via autoboxing...) Completely flawed. You could also write:

    synchronized(new Object()) {
      //thread-safe, SRSLy?
    }
    

    No two threads can enter the same synchronized block with the same lock. In this case (and similarly in your code) the lock object changes upon every execution, so synchronized effectively has no effect.

    Even if you have used a final variable (or this) for synchronization, the code is still incorrect. Two threads can first read i to temp synchronously (having the same value locally in temp), then the first assigns a new value to i (say, from 1 to 6) and the other one does the same thing (from 1 to 6).

    The synchronization must span from reading to assigning a value. Your first synchronization has no effect (reading an int is atomic) and the second as well. In my opinion, these are the correct forms:

    void synchronized incIBy5() {
      i += 5 
    }
    
    void incIBy5() {
      synchronized(this) {
        i += 5 
      }
    }
    
    void incIBy5() {
      synchronized(this) {
        int temp = i;
        i = temp + 5;
      }
    }
    
    0 讨论(0)
提交回复
热议问题