Java 为什么这段代码不抛出NullPointerException? 背景
我想理解为什么一段代码不抛出NullPointerException 源代码 考虑以下代码:Java 为什么这段代码不抛出NullPointerException? 背景,java,multithreading,nullpointerexception,thread-safety,Java,Multithreading,Nullpointerexception,Thread Safety,我想理解为什么一段代码不抛出NullPointerException 源代码 考虑以下代码: public class Agent { public List files = new ArrayList(); public void deliver() { if( files != null && files.iterator().hasNext() ) { File file = (File)files.iterator().next();
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;
}
只有一个代理
实例
问题
永远不会抛出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()
之间存在竞争条件。实际上,我不能在不引入暂停的情况下触发竞争条件(即,从随后的方法调用中分离空检查)
问题:
当空检查和用法组合在同一条语句中时,为什么第一个交付方法不抛出异常?两件事:
sleep(0)仍然停止执行(可能超过0毫秒)。基本上,即使是0睡眠也会导致该线程上的执行短暂停止,然后重新启动。这给了另一个线程运行和完成的机会,这就是为什么您能够触发竞速条件
文件应该是易变的,否则JVM被允许以这样一种方式进行优化,您可能永远不会注意到它正在改变值,因为它认为它不需要保持线程之间的一致性
您能否在适当的周围竞态条件区域发布javap
输出?当您将文件设置为volatile
时会发生什么?这是否意味着JVM可以将空检查和后续使用视为原子操作,除非显式设置为volatile
,否则线程可以具有变量的本地副本,如您的案例中的文件。因此,将文件设置为该线程之外的其他内容不会对局部变量产生影响。线程最终将检查外部修改,但在您的情况下,可能是在文件总是设置为值的情况下。Volatile将确保线程始终检查外部修改。所以:不,它不会将此视为一个原子操作,您只是幸运地在JVM中的机器上获得了示例。