清单3.15。如果没有正确发布,将面临失败风险。
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.");
}
}
我的第一个问题是为什么javac不优化if (n != n)
?
以下是我的示例
的演示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();
}
}
我的第二个问题是我做对了吗?因为在我运行演示时它不会发生异常。
更新
1.除了我的第一个问题:
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
我认为javac会优化if(n!=n)
到if(false)
并缩小它。
2.为什么我仍然在try{}catch(NullPointerException e)
之后添加if(test != null)
?
因为我认为test
之后的其他帖子可能会设置null
字段if(test!=null)
。
答案 0 :(得分:0)
首先,javac
几乎从不优化代码,你正在编译。只有当值为编译时常量时,才需要javac
来计算编译时的表达式,它本身构成编译时常量,请参阅JLS §15.28. Constant Expressions。
但是,操作在运行时得到优化,甚至缺少线程同步措施,允许优化器使用乐观假设,例如变量在两次读取之间不会改变。所以n!=n
表达式开始时评估为true
的可能性很小,因为读取之间的时间很短¹,并且在优化器启动后几乎不会true
。所以尽管表达式n!=n
并不保证总是false
,在实践中不太可能遇到true
。
当然,根据墨菲定律,当你试图挑起那个错误时,它永远不会发生,但偶尔会在客户身上发生,但永远不会重现......
¹请注意,即使第二个线程因竞争条件而读取初始0
值,n!=n
也只会失败,如果在后续版本中没有再次读取初始0
读取。