Timer引发NullPointerException:判断object非空后被置null是否可能?
问题描述
有一个名为foo的Swing类对象(每秒显示时间的窗口),核心代码如下:
Timer timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { if(object != null){ object.getProperty(); // 此处抛出NullPointerException } } }, 0, 1000);
另一个类中会执行:
foo.setObject(null);
请问是否存在执行完if(object != null)判断后、调用object.getProperty()之前,另一个类中的foo.setObject(null)被执行的情况?
回答
是的,这种情况完全可能发生,本质是多线程环境下的竞态条件导致的:
- TimerTask的
run方法运行在Timer的后台线程,而foo.setObject(null)是在另一个独立线程(比如Swing事件调度线程或业务线程)执行,两个线程会抢占CPU时间片。 - 当后台线程刚完成
object != null的非空检查,CPU时间片切换到执行setObject(null)的线程,把object置为null;等切回后台线程执行object.getProperty()时,object已经是null,自然抛出NullPointerException。
解决方法
可以通过以下几种方式避免这个问题:
- 局部变量缓存(轻量首选):把
object先赋值给线程栈内的局部变量,局部变量不会被其他线程修改,后续操作直接用这个局部变量:
Timer timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { Object localObj = object; // 缓存到局部变量 if(localObj != null){ localObj.getProperty(); } } }, 0, 1000);
- 同步锁:给
object的读写操作加同一锁,保证检查和调用的原子性:
Timer timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { synchronized (foo) { // 使用foo作为锁对象 if(object != null){ object.getProperty(); } } } }, 0, 1000); // 对应的setObject方法也要加同步 public synchronized void setObject(Object obj) { this.object = obj; }
- 原子引用:用
AtomicReference包装object,利用原子操作保证线程安全:
private AtomicReference<Object> objectRef = new AtomicReference<>(); // TimerTask中的代码 Timer timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { Object localObj = objectRef.get(); if(localObj != null){ localObj.getProperty(); } } }, 0, 1000); // 设置方法 public void setObject(Object obj) { objectRef.set(obj); }
内容的提问来源于stack exchange,提问作者Javi
相关产品推荐
相关产品推荐

