简体   繁体   中英

Does making reference variable volatile, make all its field volatile too in java?

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??

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.

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.

Eg:

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

    }

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;
    }
} 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM