views:

230

answers:

5

If variables in Java are accessed from multiple threads, one must ensure that they are safely published. This usually means using synchronizedor volatile.

I have got the impression, that some of my colleagues do not take this issue seriously, since they "never heard of volatile before and their programs have worked for years".

So my question is:

Can someone provide an example Java program/snippet, that reliably shows data visibility problems.

I think running a program and seeing the unexpected NPE or stale variable value would help more, than just theoretic explanations, that cannot be demonstrated.

Thanks a lot for your help!

Update: Just to emphasis the point again. I have read Java Concurreny in Practice and know examples that theoretically have visibility issues. What I am looking for is a way to actually demonstrate them. I am not sure, that this is actually possible, but maybe there is a jvm configuration or something similar that allows it.

A: 

Make them read the book Java Concurrency in Practice by Java concurrency guru Brian Goetz. That book is a must-read for anybody who has to write any serious concurrent software in Java!

Ofcourse saying "I've never heard of volatile and my programs have worked for years" is a dumb argument from ignorance.

Jesper
This does not even come close to answering my question.
Joe23
But that book is highly recommended anyway if you are doing concurrent programming in Java.
Jesper
Certainly, it is a great book and I have read it. What I was wondering was, if it is possible to actually _demonstrate_ such problems. This could help convince people who do not read books about Java concurrency. Probably my question should have been more precise.
Joe23
+4  A: 

The most common example brought up to emphasis the importance of using volatile is the while(keepRunning) example:

public class Test extends Thread {

    boolean keepRunning = true;

    public static void main(String[] args) throws InterruptedException {
        Test t = new Test();
        t.start();
        Thread.sleep(1000);
        t.keepRunning = false;
        System.out.println(System.currentTimeMillis() + ": keepRunning is false");
    }

    public void run() {
        while (keepRunning) 
            System.out.println(System.currentTimeMillis() + ": " + keepRunning);
    }
}

Since keepRunning may (validly) be kept in cache of the thread running the while-loop, this program may print "true" for keepRunning long after keepRunning is set to false.

Note however, that there is no reliable way of exposing race-conditions. (See my other answer.) This example may expose it under certain circumstances on certain combinations of hardware / operating system / jvm.

aioobe
On my machine this prints:1273221858765: true...1273221859140: true1273221859156: keepRunning is falseJust as expected. Are there ways to actually provoke the visibility issue? What I am looking for is a snippet, that actually shows the problem and does not have it just in theory.
Joe23
@Joe23, first, remove the calls to system time, they are not necessary. You only need to show that a 'true' prints out after another thread prints false. Second, you might not be able to create visibility problems on your developer machine/PC (fewer cores and all). I have a dual core and was able to demonstrate a visibility issue once every few hundred attempts.
Tim Bender
Run the test many times and you will hit an out of order each so often. Now the question is whether the test is actually valid or not, since there can be a race condition int the printout. Even with correct code --with volatile in place-- it might be the case that both threads call the `println` at the same time and the output might be out of order. Concurrency is hard, mainly because testing is so difficult.
David Rodríguez - dribeas
@Tim, the System.currentTimeMillis() was there to make sure that keepRunning wasn't read in the while condition, the keepRunning set to false, then printed. It's a bad example however, as @David points out.
aioobe
+2  A: 

Can someone provide an example Java program/snippet, that reliably shows data visibility problems.

No, there is no reliably example that shows data visibility problems.

The reason is that any valid execution of a program with volatile is also a valid execution of the same program without volatile. (The opposite is obviously not true though!)

aioobe
A: 

By modifying the example here by removing operations I have come up with an example that consistently fails in my environment (the thread never stops running).

// Java environment:
// java version "1.6.0_0"
// OpenJDK Runtime Environment (IcedTea6 1.6.1) (6b16-1.6.1-3ubuntu3)
// OpenJDK 64-Bit Server VM (build 14.0-b16, mixed mode)
public class Test2 extends Thread {
    boolean keepRunning = true;
    public static void main(String[] args) throws InterruptedException {
        Test2 t = new Test2();
        t.start();
        Thread.sleep(1000);
        t.keepRunning = false;
        System.out.println(System.currentTimeMillis() + ": keepRunning is false");
    }
    public void run() {
        while (keepRunning) 
        {}
    }
}

Note that this type of problems are quite dependent on the compiler/runtime/system. In particular the compiler can determine to add instructions to read the variable from memory even if it is not volatile --so the code would work--, the vm and jit can optimize away the reads from memory and use only registers, and even the processor can reorder instructions --that would not affect this case, but in other multithreaded cases it can affect the perceived state from other threads if more than one variable are modified.

David Rodríguez - dribeas
How does this expose a data visibility problem? What do you mean by "consistently fails"?
aioobe
Great! Thanks a lot. This example will work on my Windows Laptop and on Solaris 10 with a client-vm, but fails (i.e. runs forever) on Solaris with server-vm and on 64-bit Linux. Exactly what I was looking for. The best thing is, that on Solaris switching between -client/-server makes it either always fail or always run. That should drive the point home in a presentation. :)
Joe23
@aioobe, isn't it a visibility problem, because if the updated state of the field `keepRunning` is not visible to the second thread, it will run forever? Am I missing something completely?
Joe23
Aha! I see. Nice!
aioobe
A: 

I have a snippet of code for you:

package test;

public class LoopTest {

 private boolean done = false;

 /**
  * @param args
  */
 public void start() {
  System.out.println(System.getProperty("java.vm.name"));
  System.out.println(System.getProperty("java.version"));
  for (int i = 0; i < 100; i++) {
   startNewThread();
  }

  try {
   Thread.sleep(1000);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  done = true;
  System.out.println("forcing end");
 }

 private void startNewThread() {
  new Thread(new Runnable() {

   public void run() {
    long i = 0;
    while(!done) {
     i++;
     if(i % 100L == 0) {
      System.out.println("still working " + i);
     }
    }
    System.out.println("ending " + i);
   }

  }).start();
 }

 public static void main(String[] args) {
  new LoopTest().start();
 }

}

This example run in JVM Server mode generated this output on my machine:

..
..
ending 14100
still working 14800
ending 14800
still working 26500
ending 26500
still working 18200
ending 18200
still working 9400
ending 9400
still working 1300
ending 1300
still working 59500
ending 59500
still working 1700
still working 75400
ending 75400
still working 33500
ending 33500
still working 36100
ending 36100
still working 121000
ending 121000
still working 3000
ending 3000
ending 1700
still working 5900
ending 5900
still working 7800
ending 7800
still working 7800
ending 7800
still working 6800
ending 6800
still working 5300
ending 5300
still working 9100
still working 10600
ending 10600
still working 9600
ending 9600
still working 10000
ending 10000
ending 9100
still working 1700
ending 1700
..
..

Look at the "ending #" statements: all of them have a number which is a multiple of 100 which is quite unlikely to happen, i think. My interpretation is that there is an visibility issue which causes the threads to still read done == false although it already has been updated to true. After the call to the synchronized System.out.println() method with the "still working #" statement, the threads read the updated value for done and exit.

Or does anyone see a mistake in my code / interpretation?

soil7c3
Hm, the described effect persists even after 'volatile' is added to the field declaration. Therefore it seems more likely to be a timing issue, doesn't it? I assume, that the code spends more time in sysout and thus it is more likely to be stopped, while i % 100.
Joe23