Would a thread handling a subsequent network request be guaranteed to see the value of a volatile variable written during a previous request?

Viewed 96

I have this theoretical question about the Java Memory Model. Suppose I have a server with these two request handlers in the following class:

class MyHandlers {
  volatile int someFlag = 0;

  String handleFirstRequest() {
    someFlag = 1;
    return "Hello!";
  }

  String handleSecondRequest() {
    return "value of someFlag: " + someFlag;
  }
}

I also have a client. My client sends a network request that triggers executing handleFirstRequest. The client waits until the request completes. Once the first request completes, the client sends the second request that triggers handleSecondRequest.

Question: How does the Java Memory Model prevent the response to the second request from being "value of someFlag: 0"?

Notes: I understand that, in practice, the thread handling the second response will always see someFlag as 1.

If I read the JMM correctly, there is a synchronization order that is a total order, which would order the volatile read and the volatile write (someFlag = 1) in my example. If the read is subsequent to the write, then the read will see the write. Is it possible to have a case where the write is subsequent to the read? In this case, the write does not synchronizes-with the read, and there would be no happens-before relationship between the write and the read. This would lead the thread handling the second request to see someFlag as 0. Where does my understanding go wrong?

Additional thoughts (2020 Mar 2): The JMM does not refer to the concept of time. Synchronization actions are ordered according to synchronization order, but nothing in the JMM says that synchronization order is the same as order of actions sorted by time. This suggests a Java implementation may order the read of someFlag before the write even though the read occurred after the write according to the clock. It seems like the JMM only guarantees that if the volatile read is ordered after the volatile write, then writes before the volatile write are visible to reads after the volatile read.

4 Answers

If the first request completes then it means that someFlag = 1 has been executed by some thread. At that point the value of someFlag is guaranted to be visible by any other threads performing read. So when the second request comes you can be sure that it will see value of 1.

Typically, each thread has its own cache somewhere in the hardware. Reads and writes are normally did in this cache and at some later time (when the memory lines need to leave the cache), are written back into the main memory. This is the reason why two different threads might see different values for the same variable.

The volatile keyword prevents that. A volatile value is never cached and all of the reads and writes must be done in the main memory. As a bonus, the volatile value is also atomically read and written.

So, when the first thread updates the someFlag to 1, it is made visible to all threads instantly (at the cost of a lower perfomance due to caching-prevention). Then, when the second thread reads it, it will see the value given by the first thread.

Since the client waits for the completion of the first request to start the second, you established that the first request happens-before the second one.

Summing all of that, there would be no way for the second request to see someFlag as 0.

I found my answer!

Section 17.4.3 of the Java Language Specification states the following:

Sequential consistency is a very strong guarantee that is made about visibility and ordering in an execution of a program. Within a sequentially consistent execution, there is a total order over all individual actions (such as reads and writes) which is consistent with the order of the program, and each individual action is atomic and is immediately visible to every thread.

In the Java Memory model, "actions" refer to inter-thread actions, which include volatile writes. This paragraph guarantees that any Java implementation that conforms to the JLS guarantees that volatile writes will be immediately visible to other threads. In the example in the opening post, this paragraph guarantees that the volatile read can not be ordered before the volatile write.

In your description,you send request2 after request1 completed,so request2 is certein get someFlag = 1。


In another situation:thread A has just started handleFirstRequest() yet finished when thread B come to read the someFlag,B will get someFlag = 0。

Yes,it could be,but with a very very slim possibility...

In JMM,there are 8 atomic operations you may have read:

  • lock \ unlock
  • read \ write
  • load \ store
  • use \ assign

Yes each JMM operation has atomicity feature but ,they can not be one atomic within ordered JMM executions。If thread want to alter someFlag,he will read -- load -- em... and a lot of jmm operations,all the seperated operations do not hava atomicity。


And what volatile do? it use CAS to prevent Multi-thread conflict and ennable Bus could perceive any JMM write operation and then invalidate field which been written in all thread have read。When a loaded field is invalid,thread will update it immediately。

Related