Search code examples
javaconcurrencyassertionsjava-threads

How to understand an example of book java concurrency in practice?


Listing 3.15. Class at Risk of Failure if Not Properly Published.

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.");
 }
} 

My first question is why javac not optimize if (n != n)?

The following is my demo for the example

public class TestSync {
    private int n;

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

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

    private static TestSync test;
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    if(test == null) test = new TestSync(2);
                    else test = null;
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    if(test != null)
                        try {
                            test.assertSanity();
                        } catch (NullPointerException e) {

                        }
                }
            }
        }).start();
    }
}

And my second question is Did I do the right thing? Because it occurs no exception when I run the demo.

UPDATE

1.Addition to my first question: javap -c TestSync.class

public void assertSanity();
    Code:
       0: aload_0
       1: getfield      #3                  // Field n:I
       4: aload_0
       5: getfield      #3                  // Field n:I
       8: if_icmpeq     21
      11: new           #4                  // class java/lang/AssertionError
      14: dup
      15: ldc           #5                  // String This statement is false
      17: invokespecial #6                  // Method java/lang/AssertionError."<init>":(Ljava/lang/Object;)V
      20: athrow
      21: return

I thinked javac would optimize if(n!=n) to if(false) and shrink it.

2.Why I still add try{}catch(NullPointerException e) after if(test != null)?

Because I think field test may be setted null by the other thread after if(test!=null).


Solution

  • First of all, javac almost never optimizes the code, you’re compiling. Only when the values are compile-time constants, javac is required to evaluate expressions at compile-time, which itself form compile-time constants, see JLS §15.28. Constant Expressions.

    However, operations get optimized at runtime and it is even the absence of thread synchronization measures that allows the optimizer to use optimistic assumptions, like that a variable won’t change between two reads. So the n!=n expression starts with a low likelihood of ever evaluate to true due to the short time between the reads¹ and will almost never be true after the optimizer kicked in. So while the expression n!=n is not guaranteed to be always false, it’s unlikely to ever encounter it to be true in practice.

    Of course, according to Murphy’s Law, it will never happen when you try to provoke that error anyway, but once in a while at the customer, but never reproducible…

    ¹ Note that even if the second thread reads the initial 0 value due to the race condition, n!=n will only fail, if does not read the initial 0 again in the subsequent read.