Search code examples
javamultithreadingvolatile

Why can my two threads coordinate through non-volatile fields?


According to this specification, two java threads can not coordinate through non-volatile fields. Why is my code running okay?

public class TestVolatileExample {

static int pairCount = 1000;
static VolatileExample[] exps = new VolatileExample[pairCount];
static{
    for(int i = 0;i<pairCount;i++){
        exps[i] = new VolatileExample();
    }
}

@Test
public void test() throws InterruptedException{
    final int valuePair[][] = new int[pairCount][2];
    Thread[] threads = new Thread[pairCount*2];
    for(int i = 0;i<pairCount;i++){
        final int index = i;
        //final VolatileExample exp = new VolatileExample();
        //writer
        Thread writer = new Thread(new Runnable(){
            @Override
            public void run() {
                VolatileExample exp = exps[index];
                int val = new Random().nextInt(100);
                valuePair[index][0] = val;
                exp.set(val);
            }
        });
        writer.start();
        threads[i*2] = writer;
        //reader
        Thread reader = new Thread(new Runnable(){
            @Override
            public void run() {
                VolatileExample exp = exps[index];
                while(!exp.changed()){
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //System.out.println("waitting for change...");
                }
                int val = exp.get();
                valuePair[index][1] = val;
            }

        });
        reader.start();
        threads[i*2+1] = reader;
    }
    for(Thread t : threads){
        t.join();
    }
    for(int i = 0;i<pairCount;i++){
        int write = valuePair[i][0];
        int read =  valuePair[i][1];
        System.out.println(write+"," + read);
        Assert.assertEquals(write,read);
    }
 }
}
public class VolatileExample {

  private int x;
  private boolean changed = false; 

  public void set(int x){
      this.x = x;
      this.changed = true;
      System.out.println("changed...");
  }

  public int get(){
      return x;
  }

  public boolean changed(){
      return changed;
  }
}

You see, the reader thread is waiting for the value x in VolatileExample until the flag property been changed. According to Java specification, the non-volatile property,'changed', will be saved in the respective cache of each thread. But why did my program get the expected results?

I started 1000 pairs of reading and write threads, and each read thread did read the values written by the write threads.

Is there anything wrong with me?


Solution

  • The page you linked to says:

    The compiler is free to read the field this.done just once, and reuse the cached value in each execution of the loop. This would mean that the loop would never terminate, even if another thread changed the value of this.done.

    This means whether your code works or not depends on whether the compiler decides to cache your variables (doesn't work) or not (works). It is free to do so if it wants, but it doesn't have to.

    So your code may or may not work depending on things that are not under your control.