繁体   English   中英

如何模拟构造函数的竞争条件?

[英]How to simulate constructor race conditions?

我正在阅读“Java Concurrency in practice”并查看第51页的示例代码。

这表明如果一个线程具有对共享对象的引用,那么其他线程可能能够在构造函数完成执行之前访问该对象。

我试图将其付诸实践,因此我编写了这段代码,认为如果我运行足够多次RuntimeException(“World is f * cked”)就会发生。 但事实并非如此。

这是Java规范不保证某些东西的情况,但我特定的java实现保证了它吗? (java版本:Ubuntu上的1.5.0)或者我误读了书中的内容?

代码:(我期待一个例外,但它永远不会被抛出)

public class Threads {
 private Widgit w;

 public static void main(String[] s) throws Exception {
  while(true){
   Threads t = new Threads();
   t.runThreads();
  }
 }

 private void runThreads() throws Exception{
  new Checker().start();
  w = new Widgit((int)(Math.random() * 100)  + 1);
 }

 private class Checker extends Thread{
  private static final int LOOP_TIMES = 1000;

  public void run() {
   int count = 0;
   for(int i = 0; i < LOOP_TIMES; i++){
    try {
     w.checkMe();
     count++;
    } catch(NullPointerException npe){
     //ignore
    }
   }
   System.out.println("checked: "+count+" times out of "+LOOP_TIMES);
  }
 }

 private static class Widgit{
  private int n;
  private int n2;

  Widgit(int n) throws InterruptedException{
   this.n = n;
   Thread.sleep(2);
   this.n2 = n;
  }

  void checkMe(){
   if (n != n2) {
    throw new RuntimeException("World is f*cked");
   }
  }
 }

}

在构造函数完成之后才发布引用,更改Widgit如下所示:

private class Widgit{ // NOTE: Not class is not static anymore
    private int n;
    private int n2;

    Widgit(int n) throws InterruptedException{
        this.n = n;
        w = this; // publish reference
        Thread.sleep(2);
        this.n2 = n;
    }

    void checkMe(){
        if (n != n2) {
        throw new RuntimeException("World is f*cked");
    }    
}

现在应该扔。

编辑:您还应该将Widgit字段声明为volatile

 private volatile Widgit w;

那么,你需要更多地了解这些问题。 任何事情都不是“保证”的情况并非如此。 对于并发问题,除非你真的做了特定的事情来强迫问题发生,否则没有什么能得到真正的保证。 你只是依靠足够的运行应该产生的希望,但事实并非如此。 这些问题很难预测,这就是并发是一个难题。 你可以尝试在你的函数中做更多的工作,但我向你保证这些是运行时不会拯救你的真正问题。

在睡觉之前,启动一个打印n2值的新线程。 您将看到第二个线程可以在构造函数完成之前访问该对象。

以下示例在Sun JVM上演示了此内容。

/* The following prints
Incomplete initialisation of A{n=1, n2=0}
After initialisation A{n=1, n2=2}
 */
public class A {
    final int n;
    final int n2;
    public A() throws InterruptedException {
        n = 1;
        new Thread(new Runnable() {
            public void run() {
                System.out.println("Incomplete initialisation of " + A.this);
            }
        }).start();
        Thread.sleep(200);
        this.n2 = 2;
    }
    @Override
    public String toString() {
        return "A{" + "n=" + n + ", n2=" + n2 + '}';
    }
    public static void main(String... args) throws InterruptedException {
        System.out.println("After initialisation " + new A());
    }
}

这绝不会抛出一个RunTimeException ,因为你的Widgit实例变量w直到构造函数代码已执行保持为空。 当你的主线程在Widgit构造函数中休眠时,你的Checker实例会不断地遇到NullPointerException因为w变量仍为null。 当主线程完成构造时,Widgit中的两个int变量是相等的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM