简体   繁体   中英

Volatile happens-before relationship when there's mix of volatile and non-volatile fields

I am trying to understand the happens-before behavior of the volatile field when there's a mix of volatile and non-volatile fields.

Let's say there's 1 WriteThread and 5 ReadThreads, and they update/read the SharedObject.

ReadThreads call method waitToBeStopped() from the beginning and WriteThread calls the method stop() after 1 second.

public class SharedObject {

    volatile boolean stopRequested = false;
    int a = 0, b = 0, c = 0;
    int d = 0, e = 0, f = 0;

    // WriteThread calls this method
    public void stop() {
        a = 1;  
        b = 2;  
        c = 3;
        stopRequested = true;
        a = 4;  
        b = 5;
        c = 6; 
        d = 7;
        e = 8;
        f = 9;
    }

    // ReadThread calls this method
    public void waitToBeStopped() throws Exception {
        
        while(!stopRequested) {
        }
        System.out.println("Stopped now.");

        System.out.println(a + " " + b + " " + c + " " + d + " " + e + " " + f);
    }
}

When this program ends, the output is something like this. Even when I try 100+ ReadThreads, the result is always the same.

Stopped now.
Stopped now.
Stopped now.
Stopped now.
Stopped now.
4 5 6 7 8 9
4 5 6 7 8 9
4 5 6 7 8 9
4 5 6 7 8 9
4 5 6 7 8 9

Q1. Can someone explain why this always returns 4,5,6,7,8,9 and not 1,2,3,0,0,0?

My understanding about happens-before relationship was something like this:

  • WriteThread writes to a=1,b=2,c=3 happens before WriteThread writes to stopRequested
  • WriteThread writes to stopRequested happens before WriteThread writes to a=4,b=5,c=6,d=7,e=8,f=9
  • WriteThread writes to stopRequested happens before ReadThread reads stopRequested
  • ReadThread reads stopRequested happens before ReadThread reads a,b,c,d,e,f

From these 4 statements, I cannot derive one like this...

  • WriteThread writes to a=4,b=5,c=6,d=7,e=8,f=9 happens before ReadThread reads a,b,c,d,e,f

Here's the other part of the code if it helps:

public class App {
    public static void main(String[] args) throws Exception {
        SharedObject sharedObject = new SharedObject();

        for(int i =0 ; i < 5; i++) {
            Runnable rThread = new ReadThread(sharedObject);
            new Thread(rThread).start();
        }
        Runnable wThread = new WriteThread(sharedObject);
        
        new Thread(wThread).start();        

    }
}
public class WriteThread implements Runnable {

    private SharedObject sharedObject;
    
    public WriteThread(SharedObject sharedObject) {
        this.sharedObject = sharedObject;
    }
    
    public void run() {
        try {
            TimeUnit.SECONDS.sleep(1);
            sharedObject.stop();
                        
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
public class ReadThread implements Runnable {

    private SharedObject sharedObject;
    
    public ReadThread(SharedObject sharedObject) {
        this.sharedObject = sharedObject;
    }
    
    public void run() {
        try {
            sharedObject.waitToBeStopped();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }   
}

Your assumption that the writes after stopRequested = true; are not garanteed to be visible to the readers is correct. The writer is not garanteed to do those writes to shared cache/memory where they'd become visible to the readers. It could just write them to its local cache and the readers wouldn't see the updated values.

The Java language makes garantees about visibility, eg when you use a volatile variable. But it doesn't garantee that changes on a non -volatile variable won't be visible to other threads. Such writes can still be visible like here in your case. The JVM implementation, the memory consistency model of the processor and other aspects influence visibility.

Note that the JLS, and the happens-before relationship, is a specification . JVM implementations and hardware often do more than the JLS specifies, which can lead to visibilities of writes that don't have to be visible by the JLS.

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