我想了解为什么一段代码 不 会抛出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检查和用法时不会抛出异常?
答案 0 :(得分:5)
两件事:
Thread.sleep(0)仍然停止执行(可能超过0毫秒)。基本上,即使是0睡眠也会导致该线程上的执行暂时停止,然后重新启动。这使得另一个线程有机会运行和完成,这就是为什么你能够触发竞争条件。
文件应为volatile
,否则允许JVM以这样的方式进行优化,使您可能永远不会注意到它正在改变值,因为它不认为它需要保持线程之间的一致性