如何在实践中理解书籍java并发的一个例子?

时间:2016-10-20 02:41:36

标签: java concurrency assertions java-threads

  

清单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)

1 个答案:

答案 0 :(得分:0)

首先,javac几乎从不优化代码,你正在编译。只有当值为编译时常量时,才需要javac来计算编译时的表达式,它本身构成编译时常量,请参阅JLS §15.28. Constant Expressions

但是,操作在运行时得到优化,甚至缺少线程同步措施,允许优化器使用乐观假设,例如变量在两次读取之间不会改变。所以n!=n表达式开始时评估为true的可能性很小,因为读取之间的时间很短¹,并且在优化器启动后几乎不会true。所以尽管表达式n!=n并不保证总是false,在实践中不太可能遇到true

当然,根据墨菲定律,当你试图挑起那个错误时,它永远不会发生,但偶尔会在客户身上发生,但永远不会重现......

¹请注意,即使第二个线程因竞争条件而读取初始0值,n!=n也只会失败,如果在后续版本中没有再次读取初始0读取。