How to demonstrate race conditions around values that aren't published properly?

Solution 1:

I'd run this on a multiprocessor machine for a few hours and see what happens(remove the sleep if you use your Holder2). Such race conditions might be rare, or not existant on your particular machine - but atleast try to provoke these one on a million cases , by trying millions of times.

class Checker {
  private Holder h;
  public Checker() {
   h = new Holder(42);
  }

  public void check() {
    h.assertSanity();
  }

  public void create(int n) {
   h = new Holder(n);
   }

}

public class MyThread extends thread{
  private bool check;
  private final Checker c;
  public MyThread(bool check,Checker c) {
    this.check = check;
    this.c = c;
  }
    public static void main(String[] args) {
      Checker c = new Checker();
      MyThread t1 = new MyThread(false,c);  
      MyThread t2 = new MyThread(true,c);
      t1.start();
      t2.start();
      t1.join();
      t2.join();
   }
   public void run() {
     int n = 0;
     while(true) {
       if(check) 
         c.check();
       else
         c.create(n++);
    }
   }
 }
}

Solution 2:

As BobbyShaftoe said in the other thread, you can't rely on just running the code enough times to show that the error can or cannot happen. If you think about this from an Assembly level, it will be very hard for n != n as it is so few calls and relies on the process being switched out at a really precise time.

If you want to be able to show whether a concurrent system is provably valid it would be better to model it using something like Labelled Transition Systems. Try the LTSA tool if you're interested in proving concurrency or finding errors.

http://www.doc.ic.ac.uk/ltsa/

Solution 3:

In the example the that book is giving the Holder class is not directly the cause of the problem, in fact it states that:

The problem here is not the Holder class itself, but that the Holder is not properly published. However, Holder can be made immune to improper publication by declaring the n field to be final, which would make Holder immutable; see Section 3.5.2.

Just prior to this it mentions the following code, which it the subject of the problem:

// Unsafe publication
public Holder holder;
public void initialize() {
  holder = new Holder(42);
}

So to re-create it you will need to create a publisher class and two threads, one that calls initialize and one that calls the assert.

Having said that, I tried to re-create it myself and still failed to do so :(

Below is my first attempt, however there is a better explanation of the problem at http://forums.oracle.com/forums/thread.jspa?threadID=1140814&tstart=195

public class HolderTest {

    @Test
    public void testHolder() throws Exception {
    for (int i = 0; i < 1000000000; i++) {
        final CountDownLatch finished = new CountDownLatch(2);

        final HolderPublisher publisher = new HolderPublisher();

        final Thread publisherThread = new Thread(new Publisher(publisher,
            finished));
        final Thread checkerThread = new Thread(new Checker(publisher,
            finished));

        publisher.holder = null;

        publisherThread.start();
        checkerThread.start();

        finished.await();
    }
    }

    static class Publisher implements Runnable {

    private final CountDownLatch finished;
    private final HolderPublisher publisher;

    public Publisher(final HolderPublisher publisher,
        final CountDownLatch finished) {
        this.publisher = publisher;
        this.finished = finished;
    }

    @Override
    public void run() {
        try {
        publisher.initialize();
        } finally {
        finished.countDown();
        }
    }

    }

    static class Checker implements Runnable {

    private final CountDownLatch finished;
    private final HolderPublisher publisher;

    public Checker(final HolderPublisher publisher,
        final CountDownLatch finished) {
        this.publisher = publisher;
        this.finished = finished;
    }

    @Override
    public void run() {
        try {
        publisher.holder.assertSanity();
        } catch (final NullPointerException e) {
        // This isnt the error we are interested in so swallow it
        } finally {
        finished.countDown();
        }
    }

    }

    static class HolderPublisher {

    // Unsafe publication
    public Holder holder;

    public void initialize() {
        holder = new Holder(42);
    }

    }
}