Why doesn't volatile in Java update the value of a variable?

后端 未结 3 767
北恋
北恋 2021-01-25 10:36

I\'ve read that "volatile" in Java allows different threads to have access to the same field and see changes the other threads has made to that field. If that\'s the c

相关标签:
3条回答
  • 2021-01-25 11:34

    There are a couple of misunderstandings here. You seem not to properly understand what a thread is, what an instance field is and what a static field is.

    An instance field is a memory location that gets allocated once you instantiate a class (ie, a memory location gets allocated for a field d when you VolatileExample v = new VolatileExample()). To reference that memory location from within the class, you do this.d (then you can write to and read from that memory location). To reference that memory location from outside the class, it must be acessible (ie, not private), and then you'd do v.d. As you can see, each instance of a class gets its own memory location for its own field d. So, if you have 2 different instances of VolatileExample, each will have its own, independent, field d.

    A static field is a memory location that gets allocated once a class is initialized (which, forgetting about the possibility of using multiple ClassLoaders, happens exactly once). So, you can think that a static field is some kind of global variable. To reference that memory location, you'd use VolatileExample.d (accessibility also applies (ie, if it is private, it can only be done from within the class)).

    Finally, a thread of execution is a sequence of steps that will be executed by the JVM. You must not think of a thread as a class, or an instance of the class Thread, it will only get you confused. It is as simple as that: a sequence of steps.

    The main sequence of steps is what is defined in the main(...) method. It is that sequence of steps that the JVM will start executing when you launch your program.

    If you want to start a new thread of execution to run simultaneously (ie, you want a separate sequence of steps to be run concurrently), in Java you do so by creating an instance of the class Thread and calling its start() method.

    Let's modify your code a little bit so that it is easier to understand what is happening:

    public class VolatileExample extends Thread {
      private int countDown = 2;
      private volatile int d = 0;
    
      public VolatileExample(String name) {
        super(name);
      }
    
      public String toString() {
        return super.getName() + ": countDown " + countDown;
      }
    
      public void run() {
        while(true) {
          d = d + 1;
          System.out.println(this + ". Value of d is " + d);
          if(--countDown == 0) return;
        }
      }
    
      public static void main(String[] args) {
        VolatileExample ve1 = new VolatileExample("first thread");
        ve1.start();
        VolatileExample ve2 = new VolatileExample("second thread");
        ve2.start();
      }
    }
    

    The line VolatileExample ve1 = new VolatileExample("first thread"); creates an instance of VolatileExample. This will allocate some memory locations: 4 bytes for countdown and 4 bytes for d. Then you start a new thread of execution: ve1.start();. This thread of execution will access (read from and write to) the memory locations described before in this paragraph.

    The next line, VolatileExample ve2 = new VolatileExample("second thread"); creates another instance of VolatileExample, which will allocate 2 new memory locations: 4 bytes for ve2's countdown and 4 bytes for ve2's d. Then, you start a thread of execution, which will access THESE NEW memory locations, and not those described in the paragraph before this one.

    Now, with or without volatile, you see that you have two different fields d : each thread operates on a different field. Therefore, it is unreasonable for you to expect that d would get incremented to 4, since there's no single d.

    If you make d a static field, only then both threads would (supposedly) be operating on the same memory location. Only then volatile would come into play, since only then you'd be sharing a memory location between different threads.

    If you make a field volatile, you are guaranteed that writes go straight to the main memory and reads come straight from the main memory (ie, they won't get cached on some -- extremely fast -- processor-local cache, the operations would take longer but would be guaranteed to be visible to other threads).

    It wouldn't, however, guarantee that you'd see the value 4 stored on d. That's because volatile solves visibility problem, but not atomicity problems: increment = read from main memory + operation on the value + write to main memory. As you can see, 2 different threads could read the initial value (0), operate (locally) on it (obtaining 1), then writing it to the main memory (both would end up writing 1) -- the 2 increments would be perceived as only 1.

    To solve this, you must make the increment an atomic operation. To do so, you'd need to either use a synchronization mechanism -- a mutex (synchronized (...) { ... }, or an explicit lock) -- or a class designed specifically for this things: AtomicInteger.

    0 讨论(0)
  • 2021-01-25 11:34

    volatile can make sharing safe (if atomicity of a single read or write operation is sufficient), it doesn't cause sharing.

    Note that if you make d static, it is actually unspecified what value d would have, because the statement d = d + 1 is not atomic, i.e. a thread may be interrupted between reading and writing d. A synchronized block, or an AtomicInteger are the typical solutions for that.

    0 讨论(0)
  • 2021-01-25 11:36

    volatile doesn't "allow the sharing" of anything. It just prevents the variable from being cached thread local, so that changes to the variables value occur immediately. Your d variable is an instance variable and is thus owned by the instance that holds it. You'll want to re-read the threading tutorials to re-align your assumptions.

    One decent reference is here

    0 讨论(0)
提交回复
热议问题