7

背景

我想了解为什么一段代码不会引发 NullPointerException。

源代码

考虑以下代码:

public class Agent {
  public List files = new ArrayList();

  public void deliver() {
    if( files != null && files.iterator().hasNext() ) {
      File file = (File)files.iterator().next();
    }

    files = new ArrayList();
  }
}

deliver方法被重复调用,而以下代码在单独的线程中运行:

  public void run() {
    agent.files = null;
  }

只有一个agent实例。

问题

永远不会抛出 NullPointerException。

但是,当deliver方法暂停时,即使是 0 毫秒,也会按预期抛出 NullPointerException:

  public void deliver() {
    if( files != null ) {
      Thread.currentThread().sleep( 0 );

      if( files.iterator().hasNext() ) {
        File file = (File)files.iterator().next();
      }
    }

    files = new ArrayList();
  }

我的理解是,理论上,检查files == null和调用之间存在竞争条件files.iterator().hasNext()。在实践中,如果不引入暂停(即,将空检查与后续方法调用分开),我就无法触发竞争条件。

问题

为什么delivernull检查和用法结合在同一个语句中,第一种方法不抛出异常?

4

1 回答 1

5

两件事情:

  1. Thread.sleep(0) 仍然会停止执行(可能超过 0 毫秒)。基本上,即使是 0 睡眠也会导致该线程上的执行暂时停止,然后重新启动。这使另一个线程有机会运行并完成,这就是您能够触发竞争条件的原因。

  2. files 应该是volatile,否则 JVM 被允许以一种你可能永远不会注意到它正在改变值的方式进行优化,因为它认为它不需要保持线程之间的一致性。

于 2013-11-02T00:21:46.007 回答