How can I use the volatile keyword in Java correctly?

前端 未结 6 1986
没有蜡笔的小新
没有蜡笔的小新 2021-02-05 14:31

Say I have two threads and an object. One thread assigns the object:

public void assign(MyObject o) {
    myObject = o;
}

Another thread uses t

相关标签:
6条回答
  • 2021-02-05 15:12

    I am trying to understand when to use volatile and when not

    You should mostly avoid using it. Use an AtomicReference instead (or another atomic class where appropriate). The memory effects are the same and the intent is much clearer.

    I highly suggest reading the excellent Java Concurrency in Practice for a better understanding.

    0 讨论(0)
  • 2021-02-05 15:19

    I have spent quite a lot of time trying to understanding the volatile keyword. I think @aleroot has given the best and simplest example in the world.

    This is in turn my explanation for dummies (like me :-)):

    Scenario1: Assuming the stop is not declared as volatile then a given thread does and 'thinks' the following:

    1. stopWork() is called: I have to set the stop to true
    2. Great, I did it in my local stack now I have to update the main heap of JVM.
    3. Oops, JVM tells me to give a way in CPU to another thread, I have to stop for a while...
    4. OK, I am back. Now I can update the main heap with my value. Updating ...

    Scenario2: Now let the stop be declared as volatile:

    1. stopWork() is called: I have to set the stop to true
    2. Great, I did it in my local stack now I have to update the main heap of JVM.
    3. Sorry guys, I have to do (2) NOW - I am told it is volatile. I have to occupy CPU a bit longer...
    4. Updating the main heap ...
    5. OK, I am done. Now I can yield.

    No synchronization, just a simple idea...

    Why not to declare all variables volatile just in case? Because of Scenario2/Step3. It is a bit inefficient but still better than regular synchronization.

    0 讨论(0)
  • 2021-02-05 15:21

    There are some confusing comments here: to clarify, your code is incorrect as it stands, assuming two different threads call assign() and use().

    In the absence of volatile, or another happens-before relationship (for example, synchronization on a common lock) any write to myObject in assign() is not guaranteed to be seen by the thread calling use() -- not immediately, not in a timely fashion, and indeed not ever.

    Yes, volatile is one way of correcting this (assuming this is incorrect behaviour -- there are plausible situations where you don't care about this!).

    You are exactly correct that the 'use' thread can see any 'cached' value of myObject, including the one it was assigned at construction time and any intermediate value (again in the absence of other happens-before points).

    0 讨论(0)
  • 2021-02-05 15:23

    Leaving the complicated technical details behind, you can see volatile less or more as a synchronized modifier for variables. When you'd like to synchronize access to methods or blocks, then you'd usually like to use the synchronized modifier as follows:

    public synchronized void doSomething() {}
    

    If you'd like to "synchronize" access to variables, then you'd like to use the volatile modifier:

    private volatile SomeObject variable;
    

    Behind the scenes they do different things, but the effect is the same: the changes are immediately visible for the next accessing thread.

    In your specific case, I don't think that the volatile modifier has any value. The volatile does not guarantee in any way that the thread assigning the object will run before the thread using the object. It can be as good the other way round. You probably just want to do a nullcheck in use() method first.

    Update: also see this article:

    Access to the variable acts as though it is enclosed in a synchronized block, synchronized on itself. We say "acts as though" in the second point, because to the programmer at least (and probably in most JVM implementations) there is no actual lock object involved.

    0 讨论(0)
  • 2021-02-05 15:27

    Declaring a volatile Java variable means:

    • The value of this variable will never be cached thread-locally
    • Access to the variable acts as though it is enclosed in a synchronized block

    The typical and most common use of volatile is :

    public class StoppableThread extends Thread {
      private volatile boolean stop = false;
    
      public void run() {
        while (!stop) {
          // do work 
        }
      }
    
      public void stopWork() {
        stop = true;
      }
    }
    
    0 讨论(0)
  • 2021-02-05 15:32

    You can use volatile in this case. You will require volatile, synchronization around the access to the variable or some similar mechanism (like AtomicReference) to guarantee that changes made on the assignment thread are actually visible to the reading thread.

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