3

I have read that making reference variable volatile, does not make its inner fields volatile.But i tried with below example where it looks like volatile nature is applied to inner fields of class as well.

User.java:- // user class having field "flag" set as true.

public class User {

    private boolean flag=true;

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
} 

MyRunnableThread1.java:-

here i have made "user" as volatile and not its inner field "flag" as volatile

child thread is continuously in loop at "while(this.user.isFlag())".

public class MyRunnableThread1 implements Runnable {

    private String threadName;
    private  volatile User  user; 

    public MyRunnableThread1(String threadName,User user)
    {
        this.threadName=threadName; 
        this.user=user;
    } 

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public void run() {

        System.out.println("child thread:"+threadName+" started");

        while(this.user.isFlag()) {

        }
        System.out.println("child thread:"+threadName+" finished");
    }
}

ThreadDemo.java:-

in main thread we sets the value of field "flag" of "User" object as false to terminate the loop in child thread

public class ThreadDemo {

    public static void main(final String[] arguments) throws InterruptedException {

        System.out.println("main thread started");

        User user=new User(); 
        MyRunnableThread1 myRunnableThread1=new MyRunnableThread1("Thread1",user);
        Thread t=new Thread(myRunnableThread1);
        t.start();

        try {
            Thread.sleep(6000);
        }
        catch(Exception e) {
            System.out.println("exception in sleep:"+e);
        }

        myRunnableThread1.getUser().setFlag(false);

        System.out.println("main thread finished"); 
    }
}

o/p:-

main thread started child thread:Thread1 started main thread finished child thread:Thread1 finished

In above example I have made "user" variable as volatile in "MyRunnableThread1.java" class. User object has field "flag" which is true at time of instantiation. After starting the child thread where it continuously execute the loop , the main thread changes the value of field "flag" of user object to false. But here field "flag" is not volatile instead the "user" reference variable is volatile.But still here the change in value of field "flag" in main thread is reflected in child thread.It behaves as if field "flag" is also volatile. can anyone help with above issue??

Lutzi
  • 416
  • 2
  • 13
nayak0765
  • 175
  • 2
  • 15
  • 1
    http://tutorials.jenkov.com/java-concurrency/volatile.html#full-volatile-visibility-guarantee – Michael May 16 '18 at 16:29
  • 1
    Also [Java Tutoials: Atomic Variables](https://docs.oracle.com/javase/tutorial/essential/concurrency/atomicvars.html) – OldCurmudgeon May 16 '18 at 16:31
  • 2
    just FYI, it's _really_ hard to see the effects of incorrect synchronization in test/debug code, for all kinds of reasons. which is why you typically don't see these errors in your dev setup, only when you go to production and _really_ stress things. – jtahlborn May 16 '18 at 16:34
  • A variable does not have fields. The value of the `user` variable in your example is only a _reference_ to a `User` object. The `User` object itself is stored on the heap, not in the variable. – Solomon Slow May 16 '18 at 16:54
  • Any variable in Java is allowed to behave as if it were volatile even if not declared so. There is no guarantee that its value is cached locally in each thread. – DodgyCodeException May 16 '18 at 17:06

3 Answers3

4

From JLS:

8.3.1.4. volatile Fields

The Java programming language allows threads to access shared variables (§17.1). As a rule, to ensure that shared variables are consistently and reliably updated, a thread should ensure that it has exclusive use of such variables by obtaining a lock that, conventionally, enforces mutual exclusion for those shared variables.

The Java programming language provides a second mechanism, volatile fields, that is more convenient than locking for some purposes.

A field may be declared volatile, in which case the Java Memory Model ensures that all threads see a consistent value for the variable (§17.4).

But objects are not variables. Then in your case what is consistent is the value of user which means that all threads see the same reference, not that they observe the same values for its inner content.

Jean-Baptiste Yunès
  • 34,548
  • 4
  • 48
  • 69
0

You're always accessing the members through the reference to the instance:

this.user.isFlag()

This way, your loop contains a read of a volatile variable user and the loop's body is not optimized out.

Try getting the instance reference to a local variable before the loop, and you'll see the difference.

E.g.:

    User u = this.user;
    while(u.isFlag())
    {

    }
  • that doesn't technically make a difference, although it may increase the chances of seeing the problem – jtahlborn May 16 '18 at 16:33
  • Yes it does. `this.user.isFlag()` gives us a volatile read inside the loop. Therefore the loop cannot be optimized out. `u.isFlag()` is, according to Java memory model, a no-op. The flag is not volatile, so cannot change in a tight loop. –  May 16 '18 at 16:35
  • 1
    A `volatile` read prevents the loop optimization, but it does not, by itself, guarantee that the thread will see the most recent version of whatever `isFlag()` returns. The language only guarantees that if thread A reads some volatile variable, then it will be able to see whatever thread B wrote to memory _before thread B updated_ the same volatile variable. – Solomon Slow May 16 '18 at 16:50
  • @jameslarge That is correct. All we're guaranteeing is that `user.flag` will be read every iteration of the loop. A very strict implementation of java MM would prevent the changes in `flag` from being seen by other threads. In fat, if you have a tight loop setting flag, and wait for optimization, that's exactly what will happen. However, in this case, we only set the flag once. All CPUs guarantee that all threads *eventually* see data set by other threads, in no particular order and in no specific time. So the repeated read and a single set causes this thing to work. –  May 16 '18 at 16:55
  • basically what @jameslarge said. i'm not sure where you get that the flag cannot change in a tight loop. nothing about the MM says that it _can't_ change, only that it is not _required_ to change. – jtahlborn May 17 '18 at 21:04
  • @jtahlborn The closest thing I can cite in support of my position is https://docs.oracle.com/javase/specs/jls/se9/html/jls-17.html#jls-17.4.9 . The way I understand it is that any stretch of code that does not have anything that is guaranteed to make stuff from other threads visible (e.g. reading of a volatile or entering a synchronized or calling join()) should be treated as a single-threaded code. And if such single-threaded code boils down to an infinite loop, the JIT is free to optimize it as such. Hence "cannot change". –  May 18 '18 at 12:33
  • *We also define a special hang action. If behavior is described by a set of external actions including a hang action, it indicates a behavior where after the external actions are observed, the program can run for an unbounded amount of time without performing any additional external actions or terminating. Programs can hang if all threads are blocked or if the program can perform an unbounded number of actions without performing any external actions.* –  May 18 '18 at 12:34
  • i think your own comments make my point. you use the words "allowable" and "is free to". i.e. either way is a valid execution. if it is "allowable" to optimize the loop, that means the jvm _does not have to_ optimize the loop. hence, you could still get new values each time through. – jtahlborn May 18 '18 at 13:27
  • @jtahlborn Very true, JIT does not have to optimize the check away. In the actual Java 1.8 Hot Spot implementation it does. Please try changing the flag after the loop had been going for a couple seconds and see what happens. In any case, I think at this point we do understand each other :) –  May 18 '18 at 15:52
0

Do not assume that declaring a reference volatile guarantees safe publication of the members of the referenced object.

When the referent is mutable and lacks thread safety, other threads might see a partially constructed object or an object in a inconsistent state.

When the referent is immutable, declaring the reference volatile suffices to guarantee safe publication of the members of the referent. You cannot use the volatile to guarantee safe publication of mutable objects. Use of the volatile can only guarantee safe publication of primitive fields, object references, or fields of immutable object referents.

You can use volatile variables instead of locks only under a restricted set of circumstances. Both of the following criteria must be met for volatile variables to provide the desired thread-safety:

  • Writes to the variable do not depend on its current value.
  • The variable does not participate in invariants with other variables.

Summary:

User is mutable, one Thread setting the flag value does not guarantees that the new value will be visible to another Thread.

Option 1: use volatile flag:

public class User {
    private volatile boolean flag = true;
} 

Option 2: use AtomicBoolean:

public class User {
    private final AtomicBoolean flag = new AtomicBoolean(true);

    public boolean isFlag() {
        return flag.get();
    }

    public void setFlag(boolean flag) {
        this.flag.set(flag);
    }
} 

Option 3: use synchronized:

public class User {
    private boolean flag = true;

    public synchronized boolean isFlag() {
        return flag;
    }

    public synchronized void setFlag(boolean flag) {
        this.flag = flag;
    }
} 
J-Alex
  • 6,881
  • 10
  • 46
  • 64
  • Is using a `Boolean` object with a boxing setter an option? E.g. `field: private final Boolean flag = new Boolean(true);` and `setter: this.flag = new Boolean(flag)` – eja Sep 09 '19 at 20:21
  • 1
    You cannot assign a new value to the `final` field once it was instantiated. Anyway setter and getter should be `synchronized` to avoid race conditions and visibility problems. – J-Alex Sep 10 '19 at 11:33
  • Sorry, I've overseen the `final` keyword. I understand the visibility problems - but wouldn't these be atomic operations and thus thread-safe, avoiding race conditions at least? – eja Sep 10 '19 at 14:30
  • First of all "a class is thread-safe when it continues to behave correctly when accessed from multiple threads" and "possibility of incorrect results in the presence of unlucky timing called race condition" (quotes from "Java Concurrency in Practice"). Assignment of a reference is an atomic operation by itself, but visibility problems lead to data races and race conditions what leads to unpredictable behaviour of the code. Such code definitely cannot be called thread safe. – J-Alex Sep 11 '19 at 13:13