有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

多线程情况下的java“不可能”NPE

我在Android应用程序中有一些固有的异步代码。我使用的是RxJava2。类Thing不在我的控制之下(但ThingFactory在我的控制之下)。在一个方法(createThing())中,实例化一个新的ThingThing的构造函数做了一些工作,完成后,通过回调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) 个答案

  1. # 1 楼答案

    如果我理解你的评论,createThing()在工作线程中被调用。在Thing构造函数启动导致回调的事件序列之后,线程调度程序不太可能但可能会停止这个工作线程,但是在newThing()返回并分配thing之前,。如果整个回调序列在调用createThing()的线程再次运行之前运行,您将看到这个NPE

    为了验证这个理论,首先创建一个反复运行的测试来重现问题。然后更改它,以便在主线程中调用createThing(),并查看问题是否消失。这将是一种变通方法,而不是修复方法。但真正的解决方案是不在Thing的构造函数中执行工作,您说这超出了您的控制范围