问题
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)
.
回答1:
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.
来源:https://stackoverflow.com/questions/40144528/how-to-understand-an-example-of-book-java-concurrency-in-practice