views:

154

answers:

3

I am reading "Java Concurrency in practice" and looking at the example code on page 51.

According to the book this piece of code is at risk of of failure if it has not been published properly. Because I like to code examples and break them to prove how they work. I have tried to make it throw an AssertionError but have failed. (Leading me to my previous question)

Can anyone post sample code so that an AssertionError is thrown? Rule: Do not modify the Holder class.

public class Holder{
private int n;

public Holder(int n){
    this.n = n;
}

public void assertSanity(){
    if (n != n) {
        throw new AssertionError("This statement is false");
    }
}
}

I have modified the class to make it more fragile but I still can not get an AssertionError thrown.

    class Holder2{
    private int n;
    private int n2;

    public Holder2(int n) throws InterruptedException{
        this.n = n;
        Thread.sleep(200);
        this.n2 = n;
    }

    public void assertSanity(){
        if (n != n2) {
            throw new AssertionError("This statement is false");
        }
    }
    }

Is it possible to make either of the above classes throw an AssertionError? Or do we have to accept that they may occasionally do so and we can't write code to prove it?

A: 

You cant change value of n at any time by using:

  Holder h = new Holder(5);
  Field f = h.getClass().getDeclaredField("n");
  f.setAccessible(true);
  f.setInt(h, 10);
  h.assertSanity();
splix
+1  A: 

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++);
    }
   }
 }
}
leeeroy
+1  A: 

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/

Oli
I disagree with your first sentence. Running the code *can* prove that an error *can* happen, if the error happens. But you're right that no amount of testing can prove the absence of errors.
Mike Daniels
Mike you're absolutely right. I got a bit carried away with my wording.
Oli