为啥这段代码不抛出 NullPointerException?

Posted

技术标签:

【中文标题】为啥这段代码不抛出 NullPointerException?【英文标题】:Why does this code not throw a NullPointerException?为什么这段代码不抛出 NullPointerException? 【发布时间】:2013-11-13 06:53:27 【问题描述】:

背景

我想了解为什么 sn-p 代码会抛出 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() 之间存在竞争条件。在实践中,如果不引入暂停(即将空检查与后续方法调用分开),我无法触发竞争条件。

问题

为什么第一个deliver方法在null检查和用法结合在同一个语句中时不会抛出异常?

【问题讨论】:

你能在适当的比赛条件区域附近发布javap 输出吗? 当你制作filesvolatile时会发生什么? 【参考方案1】:

两件事:

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

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

【讨论】:

这是否意味着JVM可以将null检查和后续使用视为原子操作,除非明确设置为volatile 在您的情况下,线程可以拥有变量的本地副本,例如文件。因此,将文件设置为该线程之外的其他内容对局部变量没有影响。该线程最终将检查外部修改,但在您的情况下,可能是在文件始终设置为一个值的地方。 Volatile 将确保线程总是检查外部修改。所以:不,它不会将此视为原子操作,您只是在 JVM 中的机器上的示例中很幸运。

以上是关于为啥这段代码不抛出 NullPointerException?的主要内容,如果未能解决你的问题,请参考以下文章

为啥不抛出异常的代码允许捕获已检查的异常?

为啥设置 DataSource 时 ComboBox 不抛出异常?

为啥空数组引用的数组访问表达式不抛出 NullPointerException?

C++ STL栈问题:为啥栈为空时pop()不抛出异常?

为啥 JPA 重复持久方法不抛出异常?

为啥 C# 编译器不抛出 null 的逻辑比较?