多线程情况下的java“不可能”NPE
我在Android应用程序中有一些固有的异步代码。我使用的是RxJava2。类Thing
不在我的控制之下(但ThingFactory
在我的控制之下)。在一个方法(createThing()
)中,实例化一个新的Thing
Thing
的构造函数做了一些工作,完成后,通过回调onThingInitialized()
通知我们。在调用回调时,应该保证thing
存在。在回调中,我将工作安排在一个单独的线程上(在本例中,使用RxJava2,但我认为这不重要)。在这段代码中没有类似于thing = null
的地方。所以,一旦设定,就永远设定
我向它抛出了一个volatile
,因为实例确实得到了更新,但从未被置空。如果我用错了,请随意责备我
public class UsesAThing implements ThingCallbacks {
private volatile Thing thing; // I feel like I don't understand 'volatile'
// I call this method
public void createThing() {
thing = thingFactory.newThing(param1, param2);
}
// Thing's constructor does some work and notifies us when it's done
@Override
public void onThingInitialized() {
// Called on main thread, but I want to do some IO work, so:
Schedulers.io().scheduleDirect(() -> {
thing.doStuff(); // NPE!
});
}
}
那里怎么可能有NPE强>
编辑:
Thing
的构造函数异步执行其工作。正如我所说,这是在一个Android环境中,所以它实际上所做的工作是绑定到一个Service
。当Service
被绑定时,它的ServiceConnection::onServiceConnected()
回调被命中,这本身实际上触发了一个AsyncTask
,它在onPostExecute()
回调中调用onThingInitialized()
回调
编辑2:
我还应该注意到,这种NPE并非总是发生。这段代码我已经运行了数百次,但我只见过一次
编辑3:示例调用代码
我没有提供示例代码,因为它和人们想象的一样简单,但它看起来是这样的:
Flowable.just(1)
.subscribeOn(Schedulers.io())
.subscribe(i -> createThing());
# 1 楼答案
如果我理解你的评论,
createThing()
在工作线程中被调用。在Thing
构造函数启动导致回调的事件序列之后,线程调度程序不太可能但可能会停止这个工作线程,但是在newThing()
返回并分配thing
之前,。如果整个回调序列在调用createThing()
的线程再次运行之前运行,您将看到这个NPE为了验证这个理论,首先创建一个反复运行的测试来重现问题。然后更改它,以便在主线程中调用
createThing()
,并查看问题是否消失。这将是一种变通方法,而不是修复方法。但真正的解决方案是不在Thing
的构造函数中执行工作,您说这超出了您的控制范围