Amit Bhati
Amit Bhati

Reputation: 5649

How volatile variable work with the other fields?

There is a method which is initializing three variables in a sequence :

 public class Counter implements Runnable{
     private int a;
     private int b;
     private volatile int c;

    //Other code in class goes here

    private void incrementCounter(int i){
      a=10+i;
      b=11+i;
      c=12+i;
     //some other code
     }

   }

The incrementCounter(int i) method is called by a thread, after the intialization of c variable, what will be the value of a,b and c in main memory and why ?

Upvotes: 2

Views: 198

Answers (4)

Martin Andersson
Martin Andersson

Reputation: 19573

In short: The happens-before relationship created through a volatile read of a volatile write ensures that whoever reads the value of c will also observe the changes to a and b.

A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field. [...] If hb(x, y) and hb(y, z), then hb(x, z).

https://docs.oracle.com/javase/specs/jls/se11/html/jls-17.html#jls-17.4.5

[...] because the happens-before relation is transitive, all actions of a thread prior to unlocking happen-before all actions subsequent to any thread locking that monitor.

[...]

Writes and reads of volatile fields have similar memory consistency effects as entering and exiting monitors[.]

https://download.java.net/java/GA/jdk14/docs/api/java.base/java/util/concurrent/package-summary.html

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.

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

Writing to a volatile field has the same memory effect as a monitor release, and reading from a volatile field has the same memory effect as a monitor acquire. In effect, because the new memory model places stricter constraints on reordering of volatile field accesses with other field accesses, volatile or not, anything that was visible to thread A when it writes to volatile field f becomes visible to thread B when it reads f.

https://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#synchronization

Same page goes on to construct an example very much like your own:

class VolatileExample {
  int x = 0;
  volatile boolean v = false;
  public void writer() {
    x = 42;
    v = true;
  }

  public void reader() {
    if (v == true) {
      //uses x - guaranteed to see 42.
    }
  }
}

Finally, interesting pitfalls to be cautious about:

https://shipilev.net/blog/2016/close-encounters-of-jmm-kind/#pitfall-release-order-wrong

Upvotes: 1

incrementCounter(int i) method is called by a thread, after the intialization of c variable, what will be the value of a,b and c in main memory and why?

I would rephrase your question as what would be the values of a,b and c as seen by Thread B after Thread A has completed its execution.

As per the below quote from JCIP, Thread B would see the updated values for both a and b as well along with c, but it comes with a caution -

The visibility effects of volatile variables extend beyond the value of the volatile variable itself. When thread A writes to a volatile variable and subsequently thread B reads that same variable, the values of all variables that were visible to A prior to writing to the volatile variable become visible to B after reading the volatile variable. So from a memory visibility perspective, writing a volatile variable is like exiting a synchronized block and reading a volatile variable is like entering a synchronized block.

But it subsequently goes on saying that,

However, we do not recommend relying too heavily on volatile variables for visibility; code that relies on volatile variables for visibility of arbitrary state is more fragile and harder to understand than code that uses locking.

UPDATE: Removed the statements regarding atomicity of volatile variables as per Ralf's findings. Will revisit that section after doing some homework on the change history.

Upvotes: -1

Dimitar Dimitrov
Dimitar Dimitrov

Reputation: 16357

You should reason about visibility and ordering guarantees of the values of certain fields in terms of the Java Memory Model.

I'll assume that your question was meant to ask "What guarantees do I have for the values of a, b and c as observed by other threads?"

In this case, in order to get a benefit out of the volatile semantics, you need to have a volatile read in another thread that sees a specific volatile write. If that happens, the volatile read synchronizes-with that volatile write which happens-before the read, and the reading thread is guaranteed to see values for a and b no older than the values written in the thread where the observed value for c was written.

P.S. When I say "no older", I am being informal, because the JMM goes to great lengths to avoid global time ordering of all actions. If we want to be formal, we can use the synchronization order, and define "no older" as not made visible by a volatile write coming in the synchronization order before the one observed.

Upvotes: 5

Solomon Slow
Solomon Slow

Reputation: 27115

What will happen depends on the hardware platform and the JVM implementation. What must happen is specified by the the Java Language Specification (JLS).

The JLS says that some other thread U will be able to see all three updates when thread U reads c.

More specifically, the JLS says an update to the volatile c in thread T "synchronizes with" a read of c in thread U. "Synchronizes with" means that everything that happened in thread T before T updated c must become visible to thread U when thread U subsequently reads c.


Note: The "synchronizes with" also goes by the name "happens before relationship", and that sometimes confuses new programmers. When somebody says, "An update of c in one thread happens before a read of c in another thread," then the newbie thinks that it means, "My thread T will update c before my thread U reads it."

"Happens before" does not mean that at all. It only means that IF the update actually happens before the read, then...

When thread T and thread U both reach for c without any controls, that's called a data race, and if the correct outcome of your program depends on which thread wins the race, then its up to you to use some synchronization means to insure that the right thread wins.

Upvotes: 1

Related Questions